fix: Node package upgrade v1.8 (#9773)

### **PR Type**
Enhancement


___

### **Description**
- Upgrade Tailwind CSS from v3.4.4 to v4.1.18

- Bump rudder-sdk-js from 2.48.43 to 2.52.7


___



<details> <summary><h3> File Walkthrough</h3></summary>

<table><thead><tr><th></th><th align="left">Relevant
files</th></tr></thead><tbody><tr><td><strong>Dependencies</strong></td><td><table>
<tr>
  <td>
    <details>
<summary><strong>package.json</strong><dd><code>Bump dependency versions
in package.json</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
</dd></summary>
<hr>

web/package.json

<ul><li>Updated <code>tailwindcss</code> version to ^4.1.18<br> <li>
Updated <code>rudder-sdk-js</code> version to ^2.52.7</ul>


</details>


  </td>
<td><a
href="https://github.com/openobserve/openobserve/pull/9773/files#diff-b861012a5dd72b8a9f3281b7cf09f5a779c98569d040b1bbc1db50f1b15e7cce">+2/-2</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>
</table></td></tr></tr></tbody></table>

</details>

___
This commit is contained in:
Bhargav
2025-12-29 20:00:34 +05:30
committed by GitHub
parent 780977af2f
commit 890e8d7669
365 changed files with 4004 additions and 3892 deletions
+1 -1
View File
@@ -3329,7 +3329,7 @@ export class LogsPage {
for (const row of rows) {
const text = row.textContent;
const colorDiv = row.querySelector('div[class*="tw-absolute"][class*="tw-left-0"]');
const colorDiv = row.querySelector('div[class*="tw:absolute"][class*="tw:left-0"]');
if (!colorDiv) continue;
@@ -123,8 +123,8 @@ export class PipelinesPage {
this.addConditionDeleteBtn = page.locator('[data-test="add-condition-delete-btn"]');
this.scheduledAlertTabs = page.locator('[data-test="scheduled-alert-tabs"]');
this.nestedGroups = page.locator('.el-border');
this.operatorLabels = page.locator('span.tw-lowercase');
this.firstConditionLabel = page.locator('.tw-flex.tw-items-start.tw-gap-1').first().locator('span').first();
this.operatorLabels = page.locator('span.tw\\:lowercase');
this.firstConditionLabel = page.locator('.tw\\:flex.tw\\:items-start.tw\\:gap-1').first().locator('span').first();
this.noteContainer = page.locator('.note-container');
this.noteHeading = page.locator('.note-heading');
this.noteInfo = page.locator('.note-info');
+688 -555
View File
File diff suppressed because it is too large Load Diff
+4 -2
View File
@@ -68,7 +68,7 @@
"reodotdev": "^1.0.0",
"rollup-plugin-node-polyfills": "^0.2.1",
"rollup-plugin-polyfill-node": "^0.13.0",
"rudder-sdk-js": "2.48.43",
"rudder-sdk-js": "^2.52.7",
"sql-formatter": "^15.6.5",
"uuid": "^13.0.0",
"vue": "^3.5.14",
@@ -83,7 +83,9 @@
"@cypress/webpack-dev-server": "^5.4.1",
"@quasar/vite-plugin": "^1.9.0",
"@rushstack/eslint-patch": "^1.10.5",
"@tailwindcss/postcss": "^4.1.18",
"@tailwindcss/typography": "^0.5.16",
"@tailwindcss/vite": "^4.1.18",
"@types/axios": "^0.14.4",
"@types/d3-force": "^3.0.10",
"@types/d3-scale": "^4.0.8",
@@ -124,7 +126,7 @@
"sass": "^1.77.8",
"sass-loader": "^16.0.6",
"start-server-and-test": "^2.0.4",
"tailwindcss": "^3.4.4",
"tailwindcss": "^4.1.18",
"typescript": "~5.9.3",
"vite": "^7.3.0",
"vite-plugin-istanbul": "^7.2.1",
+1 -1
View File
@@ -1,7 +1,7 @@
/* eslint-disable no-undef */
export default {
plugins: {
tailwindcss: {},
'@tailwindcss/postcss': {},
autoprefixer: {},
},
};
+7 -7
View File
@@ -57,7 +57,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
/>
</template>
<template v-slot:header="props">
<q-tr :props="props" class="!tw-bg-[var(--o2-table-header-bg)]">
<q-tr :props="props" class="!tw:bg-[var(--o2-table-header-bg)]">
<!-- Add checkbox header when selection is enabled -->
<q-th auto-width v-if="selection === 'multiple'">
<q-checkbox
@@ -72,15 +72,15 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
:class="col.classes || ''"
:props="props"
:style="col.style"
class="!tw-bg-[var(--o2-table-header-bg)]"
class="tw:bg-[var(--o2-table-header-bg)]!"
>
{{ col.label }}
</q-th>
</q-tr>
</template>
<template #top="scope" v-if="!hideTopPagination">
<div class="tw-flex tw-items-center tw-justify-between tw-w-full q-py-xs ">
<span class="tw-font-bold tw-text-[14px] tw-w-full q-pa-none">
<div class="tw:flex tw:items-center tw:justify-between tw:w-full q-py-xs ">
<span class="tw:font-bold tw:text-[14px] tw:w-full q-pa-none">
{{ rows.length }} {{ title }}
</span>
<QTablePagination
@@ -147,9 +147,9 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
</q-tr>
</template>
<template #bottom="scope">
<div class="tw-flex tw-items-center tw-justify-between tw-w-full tw-h-[48px]">
<div class="tw-flex tw-items-center tw-gap-2">
<div v-if="showBottomPaginationWithTitle" class="o2-table-footer-title tw-flex tw-items-center tw-w-[100px] tw-mr-md">
<div class="tw:flex tw:items-center tw:justify-between tw:w-full tw:h-[48px]">
<div class="tw:flex tw:items-center tw:gap-2">
<div v-if="showBottomPaginationWithTitle" class="o2-table-footer-title tw:flex tw:items-center tw:w-[100px] tw:mr-md">
{{ resultTotal }} {{ title }}
</div>
<slot name="bottom-actions" :scope="scope"></slot>
+8 -8
View File
@@ -18,28 +18,28 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<q-dialog>
<q-card
data-test="dialog-box"
:class="warningMessage && warningMessage.length > 0 ? 'tw-w-[500px]' : 'tw-w-[240px]'"
:class="warningMessage && warningMessage.length > 0 ? 'tw:w-[500px]' : 'tw:w-[240px]'"
>
<q-card-section class="confirmBody">
<div class="head">{{ title }}</div>
<div class="para">{{ message }}</div>
<div v-if="warningMessage && warningMessage.length > 0" class="tw-mt-4">
<div v-if="warningMessage && warningMessage.length > 0" class="tw:mt-4">
<q-banner :class="[
'tw-border-l-4 tw-p-4 tw-rounded',
'tw:border-l-4 tw:p-4 tw:rounded',
store.state.theme === 'dark'
? 'tw-bg-gray-800/60 tw-border-yellow-600/70'
: 'tw-bg-orange-50 tw-border-orange-400'
? 'tw:bg-gray-800/60 tw:border-yellow-600/70'
: 'tw:bg-orange-50 tw:border-orange-400'
]">
<template v-slot:avatar>
<q-icon
name="warning"
:class="store.state.theme === 'dark' ? 'tw-text-yellow-500/80' : 'tw-text-orange-500'"
:class="store.state.theme === 'dark' ? 'tw:text-yellow-500/80' : 'tw:text-orange-500'"
size="24px"
/>
</template>
<div :class="[
'tw-font-medium tw-text-sm tw-leading-relaxed tw-text-left',
store.state.theme === 'dark' ? 'tw-text-gray-300' : 'tw-text-orange-800'
'tw:font-medium tw:text-sm tw:leading-relaxed tw:text-left',
store.state.theme === 'dark' ? 'tw:text-gray-300' : 'tw:text-orange-800'
]">
{{ warningMessage }}
</div>
+1 -1
View File
@@ -24,7 +24,7 @@
no-route-dismiss
>
<q-tab-panels
class="tw-flex tw-justify-between"
class="tw:flex tw:justify-between"
v-model="picker.activeTab"
>
<q-tab-panel name="relative" class="q-pa-none">
+3 -3
View File
@@ -204,8 +204,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<table v-if="!hideRelativeTime" class="q-px-md startEndTime">
<tbody>
<tr>
<td class="label tw-px-2">Start time</td>
<td class="label tw-px-2">End time</td>
<td class="label tw:px-2">Start time</td>
<td class="label tw:px-2">End time</td>
</tr>
<tr>
<td>
@@ -324,7 +324,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<q-separator class="q-my-sm" />
<q-btn
data-test="date-time-apply-btn"
class="q-pa-none o2-primary-button tw-h-[30px] element-box-shadow"
class="q-pa-none o2-primary-button tw:h-[30px] element-box-shadow"
no-caps
size="sm"
@click="saveDate(null)"
+7 -7
View File
@@ -38,13 +38,13 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
store.state.zoConfig.hasOwnProperty('custom_logo_text') &&
store.state.zoConfig?.custom_logo_text != ''
"
class="text-h6 text-bold q-pa-none cursor-pointer q-mr-sm tw-flex tw-items-center"
class="text-h6 text-bold q-pa-none cursor-pointer q-mr-sm tw:flex tw:items-center"
@click="goToHome"
>{{ store.state.zoConfig.custom_logo_text }}</span
>
<!-- Custom logo image - shows appropriate logo based on current theme -->
<div class="tw-flex tw-items-center">
<div class="tw:flex tw:items-center">
<!-- Dark mode: Show dark logo, fallback to light logo -->
<img
v-if="
@@ -200,7 +200,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
@mouseenter="handleMouseEnter"
@mouseleave="handleMouseLeave"
>
<div class="row items-center no-wrap tw-gap-2">
<div class="row items-center no-wrap tw:gap-2">
<img :src="getBtnLogo" class="header-icon ai-icon" />
</div>
</q-btn>
@@ -212,10 +212,10 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
dense
no-caps
flat
class="tw-text-ellipsis tw-overflow-hidden"
class="tw:text-ellipsis tw:overflow-hidden"
>
<div class="row items-center no-wrap full-width">
<div class="col tw-truncate">
<div class="col tw:truncate">
{{ userClickedOrg?.label || "" }}
</div>
<q-icon name="arrow_drop_down" class="q-ml-xs" />
@@ -303,7 +303,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<template v-slot:no-data>
<div
data-test="organization-menu-no-data"
class="text-center q-pa-sm tw-w-full tw-flex tw-justify-center"
class="text-center q-pa-sm tw:w-full tw:flex tw:justify-center"
>
No organizations found
</div>
@@ -475,7 +475,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<q-icon size="xs" name="language" class="padding-none" />
</q-item-section>
<q-item-section>
<q-item-label class="tw-max-w-[150px]">{{
<q-item-label class="tw:max-w-[150px]">{{
t("menu.language")
}}</q-item-label>
</q-item-section>
+19 -19
View File
@@ -3,14 +3,14 @@
>
<div v-if="isOpen" class="chat-content-wrapper" :class="store.state.theme == 'dark' ? 'dark-mode' : 'light-mode'">
<div class="chat-header" :style="{ height: headerHeight ? headerHeight + 'px' : '' }">
<div class="chat-title tw-flex tw-justify-between tw-items-center tw-w-full">
<div class="chat-title tw:flex tw:justify-between tw:items-center tw:w-full">
<div class="tw-flex tw-items-center tw-gap-2">
<div class="tw:flex tw:items-center tw:gap-2">
<q-avatar size="24px">
<img :src="o2AiTitleLogo" />
</q-avatar>
<div class="tw-flex tw-items-center">
<span class="tw-mr-[5.5px]">O2 AI
<div class="tw:flex tw:items-center">
<span class="tw:mr-[5.5px]">O2 AI
</span>
<span class="o2-ai-beta-text"
>Beta</span>
@@ -32,7 +32,7 @@
dense
filled
borderless
class="tw-mb-2"
class="tw:mb-2"
>
<template #prepend>
<q-icon name="search" />
@@ -71,7 +71,7 @@
</div>
</div>
</div>
<q-separator class="tw-bg-[#DBDBDB]" />
<q-separator class="tw:bg-[#DBDBDB]" />
<!-- History Panel -->
<q-dialog v-model="showHistory" position="right">
@@ -109,9 +109,9 @@
<div class="chat-content " :class="store.state.theme == 'dark' ? 'dark-mode' : 'light-mode'">
<div class="messages-container " ref="messagesContainer" @scroll="checkIfShouldAutoScroll">
<div v-if="chatMessages.length === 0" class="welcome-section ">
<div class="tw-flex tw-flex-col tw-items-center tw-justify-center tw-h-full ">
<div class="tw:flex tw:flex-col tw:items-center tw:justify-center tw:h-full ">
<img :src="o2AiTitleLogo" />
<span class="tw-text-[14px] tw-font-[600] tw-text-center">AI native observability</span>
<span class="tw:text-[14px] tw:font-[600] tw:text-center">AI native observability</span>
</div>
</div>
<div v-for="(message, index) in processedMessages"
@@ -140,16 +140,16 @@
color="primary"
@click="copyToClipboard(block.content)"
>
<div class="tw-flex tw-items-center">
<div class="tw:flex tw:items-center">
<q-icon size="16px" name="content_copy" />
<span class="tw-ml-1" >Copy</span>
<span class="tw:ml-1" >Copy</span>
</div>
</q-btn>
</div>
<span class="generated-code-block">
<code :class="['hljs', block.language]" v-html="block.highlightedContent"></code>
</span>
<div class="code-block-footer code-block-theme tw-flex tw-items-center tw-justify-between tw-w-full">
<div class="code-block-footer code-block-theme tw:flex tw:items-center tw:justify-between tw:w-full">
<q-btn
flat
dense
@@ -158,12 +158,12 @@
color="primary"
@click="retryGeneration(message)"
>
<div class="tw-flex tw-items-center">
<div class="tw:flex tw:items-center">
<q-icon size="16px" name="refresh" />
<span class="tw-ml-1" >Retry</span>
<span class="tw:ml-1" >Retry</span>
</div>
</q-btn>
<div v-if="false" class="tw-flex tw-items-center tw-gap-2">
<div v-if="false" class="tw:flex tw:items-center tw:gap-2">
<q-btn flat dense :icon="outlinedThumbUpOffAlt" color="primary" @click="likeCodeBlock(message)" />
<q-btn flat dense :icon="outlinedThumbDownOffAlt" color="primary" @click="dislikeCodeBlock(message)" />
</div>
@@ -200,7 +200,7 @@
</q-btn>
</div>
</div>
<div class="chat-input-wrapper tw-flex tw-flex-col q-ma-md" @click="focusInput">
<div class="chat-input-wrapper tw:flex tw:flex-col q-ma-md" @click="focusInput">
<q-input
ref="chatInput"
v-model="inputMessage"
@@ -217,7 +217,7 @@
flat
>
</q-input>
<div class="tw-flex tw-items-center tw-justify-end tw-mt-2 tw-gap-2" :class="store.state.theme == 'dark' ? 'dark-mode-bottom-bar' : 'light-mode-bottom-bar'">
<div class="tw:flex tw:items-center tw:justify-end tw:mt-2 tw:gap-2" :class="store.state.theme == 'dark' ? 'dark-mode-bottom-bar' : 'light-mode-bottom-bar'">
<q-select
v-model="selectedModel"
:options="availableModels"
@@ -237,7 +237,7 @@
</q-select>
<!-- Debug info - remove this later -->
<!-- <div class="tw-text-xs tw-text-gray-500">Loading: {{ isLoading }}, Input: {{ inputMessage.length }}</div> -->
<!-- <div class="tw:text-xs tw:text-gray-500">Loading: {{ isLoading }}, Input: {{ inputMessage.length }}</div> -->
<!-- Send button - shown when not loading -->
<q-btn
@@ -247,7 +247,7 @@
round
dense
flat
class="tw-ml-1 send-button"
class="tw:ml-1 send-button"
>
<q-icon name="send" size="16px" color="white" />
</q-btn>
@@ -259,7 +259,7 @@
round
dense
flat
class="tw-ml-1 stop-button"
class="tw:ml-1 stop-button"
>
<q-icon name="stop" size="16px" color="white" />
</q-btn>
+1 -1
View File
@@ -151,7 +151,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<!-- Note at the bottom -->
<q-card-section class="q-pt-none q-pb-sm">
<q-separator class="q-mb-sm" />
<div class="text-caption text-grey-7 tw-flex tw-items-start q-gutter-xs">
<div class="text-caption text-grey-7 tw:flex tw:items-start q-gutter-xs">
<q-icon name="info_outline" size="14px" class="q-mt-xs" />
<span>Theme preferences are stored locally on this device and will not sync across different browsers or devices.</span>
</div>
+2 -2
View File
@@ -36,7 +36,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<template #before>
<div class="sql-query-pane full-height">
<div
class="pane-header q-pa-sm tw-px-[1rem] row items-center"
class="pane-header q-pa-sm tw:px-[1rem] row items-center"
:class="store.state.theme === 'dark' ? 'pane-header-dark' : 'pane-header-light'"
>
<q-icon name="code" size="20px" class="q-mr-sm" />
@@ -58,7 +58,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<template #after>
<div class="explain-results-pane full-height">
<div
class="pane-header q-pa-sm tw-px-[1rem] row items-center"
class="pane-header q-pa-sm tw:px-[1rem] row items-center"
:class="store.state.theme === 'dark' ? 'pane-header-dark' : 'pane-header-light'"
>
<div class="text-subtitle1 text-weight-medium">
+1 -1
View File
@@ -42,7 +42,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
</q-radio>
</q-card-section>
<q-card-actions class="resume-pipeline-dialog-actions q-pa-none tw-flex tw-justify-center" style="padding: 0px;">
<q-card-actions class="resume-pipeline-dialog-actions q-pa-none tw:flex tw:justify-center" style="padding: 0px;">
<q-btn
v-close-popup
unelevated
+22 -22
View File
@@ -2,14 +2,14 @@
<div class="chat-container" :class="store.state.theme == 'dark' ? 'dark-mode' : 'light-mode'">
<div class="chat-content-wrapper" :class="store.state.theme == 'dark' ? 'dark-mode' : 'light-mode'">
<div class="chat-header" :style="{ height: headerHeight ? headerHeight + 'px' : '' }">
<div class="chat-title tw-flex tw-justify-between tw-items-center tw-w-full">
<div class="chat-title tw:flex tw:justify-between tw:items-center tw:w-full">
<div class="tw-flex tw-items-center tw-gap-2">
<div class="tw:flex tw:items-center tw:gap-2">
<q-avatar size="24px">
<img :src="o2AiTitleLogo" />
</q-avatar>
<div class="tw-flex tw-items-center">
<span class="tw-mr-[5.5px]">SRE Assistant</span>
<div class="tw:flex tw:items-center">
<span class="tw:mr-[5.5px]">SRE Assistant</span>
</div>
</div>
@@ -28,7 +28,7 @@
dense
filled
borderless
class="tw-mb-2"
class="tw:mb-2"
>
<template #prepend>
<q-icon name="search" />
@@ -67,7 +67,7 @@
</div>
</div>
</div>
<q-separator class="tw-bg-[#DBDBDB]" />
<q-separator class="tw:bg-[#DBDBDB]" />
<!-- History Panel -->
<q-dialog v-model="showHistory" position="right">
@@ -106,22 +106,22 @@
<div class="messages-container " ref="messagesContainer" @scroll="checkIfShouldAutoScroll">
<!-- Context annotation -->
<div v-if="contextData" class="context-annotation" :class="store.state.theme == 'dark' ? 'context-annotation-dark' : 'context-annotation-light'">
<q-icon name="info" size="14px" class="tw-mr-1" />
<span class="tw-text-xs">
<q-icon name="info" size="14px" class="tw:mr-1" />
<span class="tw:text-xs">
<template v-if="contextType === 'alert'">
Alert: <strong>{{ contextData.name }}</strong>
</template>
<template v-else-if="contextType === 'incident'">
Incident: <strong>{{ contextData.title || 'Untitled' }}</strong>
<span v-if="contextData.severity" class="tw-ml-2 tw-opacity-75">{{ contextData.severity }}</span>
<span v-if="contextData.severity" class="tw:ml-2 tw:opacity-75">{{ contextData.severity }}</span>
</template>
</span>
</div>
<div v-if="chatMessages.length === 0" class="welcome-section ">
<div class="tw-flex tw-flex-col tw-items-center tw-justify-center tw-h-full ">
<div class="tw:flex tw:flex-col tw:items-center tw:justify-center tw:h-full ">
<img :src="o2AiTitleLogo" />
<span class="tw-text-[14px] tw-font-[600] tw-text-center">AI native observability</span>
<span class="tw:text-[14px] tw:font-[600] tw:text-center">AI native observability</span>
</div>
</div>
<div v-for="(message, index) in processedMessages"
@@ -150,16 +150,16 @@
color="primary"
@click="copyToClipboard(block.content)"
>
<div class="tw-flex tw-items-center">
<div class="tw:flex tw:items-center">
<q-icon size="16px" name="content_copy" />
<span class="tw-ml-1" >Copy</span>
<span class="tw:ml-1" >Copy</span>
</div>
</q-btn>
</div>
<span class="generated-code-block">
<code :class="['hljs', block.language]" v-html="block.highlightedContent"></code>
</span>
<div class="code-block-footer code-block-theme tw-flex tw-items-center tw-justify-between tw-w-full">
<div class="code-block-footer code-block-theme tw:flex tw:items-center tw:justify-between tw:w-full">
<q-btn
flat
dense
@@ -168,12 +168,12 @@
color="primary"
@click="retryGeneration(message)"
>
<div class="tw-flex tw-items-center">
<div class="tw:flex tw:items-center">
<q-icon size="16px" name="refresh" />
<span class="tw-ml-1" >Retry</span>
<span class="tw:ml-1" >Retry</span>
</div>
</q-btn>
<div v-if="false" class="tw-flex tw-items-center tw-gap-2">
<div v-if="false" class="tw:flex tw:items-center tw:gap-2">
<q-btn flat dense :icon="outlinedThumbUpOffAlt" color="primary" @click="likeCodeBlock(message)" />
<q-btn flat dense :icon="outlinedThumbDownOffAlt" color="primary" @click="dislikeCodeBlock(message)" />
</div>
@@ -185,7 +185,7 @@
</div>
</div>
</div>
<div v-if="isLoading" id="loading-indicator" class="tw-flex tw-items-center tw-gap-2 tw-p-4">
<div v-if="isLoading" id="loading-indicator" class="tw:flex tw:items-center tw:gap-2 tw:p-4">
<q-spinner-dots color="primary" size="2em" />
<span style="font-size: 14px; opacity: 0.7;">{{ currentObservingMessage }}</span>
</div>
@@ -210,7 +210,7 @@
</q-btn>
</div>
</div>
<div class="chat-input-wrapper tw-flex tw-flex-col q-ma-md" @click="focusInput">
<div class="chat-input-wrapper tw:flex tw:flex-col q-ma-md" @click="focusInput">
<q-input
ref="chatInput"
v-model="inputMessage"
@@ -227,7 +227,7 @@
flat
>
</q-input>
<div class="tw-flex tw-items-center tw-justify-end tw-mt-2 tw-gap-2" :class="store.state.theme == 'dark' ? 'dark-mode-bottom-bar' : 'light-mode-bottom-bar'">
<div class="tw:flex tw:items-center tw:justify-end tw:mt-2 tw:gap-2" :class="store.state.theme == 'dark' ? 'dark-mode-bottom-bar' : 'light-mode-bottom-bar'">
<!-- Send button - shown when not loading -->
<q-btn
v-if="!isLoading"
@@ -236,7 +236,7 @@
round
dense
flat
class="tw-ml-1 send-button"
class="tw:ml-1 send-button"
>
<q-icon name="send" size="16px" color="white" />
</q-btn>
@@ -248,7 +248,7 @@
round
dense
flat
class="tw-ml-1 stop-button"
class="tw:ml-1 stop-button"
>
<q-icon name="stop" size="16px" color="white" />
</q-btn>
@@ -16,8 +16,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<template>
<div class="feature-comparison-wrapper">
<div class="feature-comparison-header tw-mb-6">
<div class="tw-flex tw-items-center tw-gap-3 tw-mb-3">
<div class="feature-comparison-header tw:mb-6">
<div class="tw:flex tw:items-center tw:gap-3 tw:mb-3">
<div class="icon-wrapper" :class="store.state.theme === 'dark' ? 'icon-wrapper-dark' : 'icon-wrapper-light'">
<q-icon name="compare_arrows" size="24px" />
</div>
@@ -18,13 +18,13 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<!-- eslint-disable vue/attribute-hyphenation -->
<template>
<div data-test="action-scripts-list-page">
<div v-if="!showAddActionScriptDialog" class="tw-w-full tw-h-full tw-px-[0.625rem] tw-pb-[0.625rem] q-pt-xs">
<div class="card-container tw-mb-[0.625rem]">
<div class="tw-flex tw-justify-between tw-items-center tw-px-4 tw-py-3 tw-w-full tw-h-[68px]">
<div class="tw-font-[600] tw-text-[20px]" data-test="alerts-list-title">
<div v-if="!showAddActionScriptDialog" class="tw:w-full tw:h-full tw:px-[0.625rem] tw:pb-[0.625rem] q-pt-xs">
<div class="card-container tw:mb-[0.625rem]">
<div class="tw:flex tw:justify-between tw:items-center tw:px-4 tw:py-3 tw:w-full tw:h-[68px]">
<div class="tw:font-[600] tw:text-[20px]" data-test="alerts-list-title">
{{ t("actions.header") }}
</div>
<div class="tw-full-width tw-flex tw-items-center tw-justify-end">
<div class="tw:full-width tw:flex tw:items-center tw:justify-end">
<q-input
v-model="filterQuery"
borderless
@@ -39,7 +39,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
</q-input>
<q-btn
data-test="action-list-add-btn"
class="q-ml-sm o2-primary-button tw-h-[36px]"
class="q-ml-sm o2-primary-button tw:h-[36px]"
flat
no-caps
:label="t(`actions.add`)"
@@ -48,8 +48,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
</div>
</div>
</div>
<div class="tw-w-full tw-h-full tw-pb-[0.625rem]">
<div class="card-container tw-h-[calc(100vh-124px)]">
<div class="tw:w-full tw:h-full tw:pb-[0.625rem]">
<div class="card-container tw:h-[calc(100vh-124px)]">
<q-table
data-test="action-scripts-table"
ref="qTable"
@@ -123,15 +123,15 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
</template>
<template #bottom="scope">
<div class="tw-flex tw-items-center tw-justify-between tw-w-full tw-h-[48px]">
<div class="tw-flex tw-items-center tw-gap-2">
<div class="o2-table-footer-title tw-flex tw-items-center tw-w-[80px] tw-mr-md">
<div class="tw:flex tw:items-center tw:justify-between tw:w-full tw:h-[48px]">
<div class="tw:flex tw:items-center tw:gap-2">
<div class="o2-table-footer-title tw:flex tw:items-center tw:w-[80px] tw:mr-md">
{{ resultTotal }} {{ t('actions.header') }}
</div>
<q-btn
v-if="selectedActionScripts.length > 0"
data-test="action-scripts-bulk-delete-btn"
class="flex items-center q-mr-sm no-border o2-secondary-button tw-h-[36px]"
class="flex items-center q-mr-sm no-border o2-secondary-button tw:h-[36px]"
:class="
store.state.theme === 'dark'
? 'o2-secondary-button-dark'
@@ -142,7 +142,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
@click="openBulkDeleteDialog"
>
<q-icon name="delete" size="16px" />
<span class="tw-ml-2">Delete</span>
<span class="tw:ml-2">Delete</span>
</q-btn>
</div>
<QTablePagination
@@ -185,7 +185,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
</div>
</div>
<template v-else>
<div class="tw-w-full">
<div class="tw:w-full">
<EditScript
:isUpdated="isUpdated"
@update:list="refreshList"
+18 -18
View File
@@ -16,9 +16,9 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<template>
<div data-test="add-action-script-section">
<div class="tw-w-full tw-h-full tw-px-[0.625rem] tw-pb-[0.625rem] q-pt-xs">
<div class="tw:w-full tw:h-full tw:px-[0.625rem] tw:pb-[0.625rem] q-pt-xs">
<div class="card-container">
<div class="tw-flex tw-items-center tw-justify-between tw-py-3 tw-pl-4 tw-pr-2 tw-h-[68px]">
<div class="tw:flex tw:items-center tw:justify-between tw:py-3 tw:pl-4 tw:pr-2 tw:h-[68px]">
<div
data-test="add-action-script-back-btn"
@@ -42,8 +42,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
</div>
</div>
<div class="tw-w-full tw-h-full tw-px-[0.625rem] tw-pb-[0.625rem]">
<div class="card-container tw-h-[calc(100vh-192px)] tw-overflow-auto">
<div class="tw:w-full tw:h-full tw:px-[0.625rem] tw:pb-[0.625rem]">
<div class="card-container tw:h-[calc(100vh-192px)] tw:overflow-auto">
<div ref="addAlertFormRef" class="q-px-lg q-pb-md" style="width: 1024px">
<q-form
class="create-report-form"
@@ -102,7 +102,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
v-model="formData.type"
:label="t('common.type') + ' *'"
:options="actionTypes"
class="showLabelOnTop no-case tw-w-[400px]"
class="showLabelOnTop no-case tw:w-[400px]"
stack-label
map-options
emit-value
@@ -141,7 +141,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
v-model="formData.codeZip"
:label="t('actions.zipFile') + ' *'"
bg-color="input-bg"
class="tw-w-[300px] q-pt-md q-pb-sm showLabelOnTop lookup-table-file-uploader"
class="tw:w-[300px] q-pt-md q-pb-sm showLabelOnTop lookup-table-file-uploader"
stack-label
outlined
dense
@@ -198,7 +198,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<q-btn
data-test="add-action-script-step1-continue-btn"
@click="step = 2"
class="o2-primary-button tw-h-[36px] q-mt-sm"
class="o2-primary-button tw:h-[36px] q-mt-sm"
:class="store.state.theme === 'dark' ? 'o2-primary-button-dark' : 'o2-primary-button-light'"
flat
:label="'Continue'"
@@ -225,7 +225,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
{{ t("actions.frequency") }} *
</div>
<div
class="q-pa-xs el-border-radius el-border tw-w-fit"
class="q-pa-xs el-border-radius el-border tw:w-fit"
>
<template v-for="visual in frequencyTabs" :key="visual.value">
<q-btn
@@ -327,7 +327,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
:label="t('actions.timezone') + ' *'"
:loading="isFetchingServiceAccounts"
:popup-content-style="{ textTransform: 'lowercase' }"
class="showLabelOnTop no-case tw-mb-[2.4rem]"
class="showLabelOnTop no-case tw:mb-[2.4rem]"
borderless
stack-label
dense
@@ -354,7 +354,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
data-test="add-action-script-step2-back-btn"
@click="step = 1"
flat
class="o2-secondary-button tw-h-[36px] q-ml-sm"
class="o2-secondary-button tw:h-[36px] q-ml-sm"
:class="store.state.theme === 'dark' ? 'o2-secondary-button-dark' : 'o2-secondary-button-light'"
:label="'Back'"
no-caps
@@ -362,7 +362,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<q-btn
data-test="add-action-script-step2-continue-btn"
@click="step = 3"
class="o2-primary-button tw-h-[36px] q-ml-md "
class="o2-primary-button tw:h-[36px] q-ml-md "
:class="store.state.theme === 'dark' ? 'o2-primary-button-dark' : 'o2-primary-button-light'"
flat
:label="'Continue'"
@@ -431,7 +431,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
data-test="add-action-script-step3-back-btn"
@click="step = formData.type === 'scheduled' ? 2 : 1"
flat
class="o2-secondary-button tw-h-[36px]"
class="o2-secondary-button tw:h-[36px]"
:class="store.state.theme === 'dark' ? 'o2-secondary-button-dark' : 'o2-secondary-button-light'"
:label="'Back'"
no-caps
@@ -439,7 +439,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<q-btn
data-test="add-action-script-step3-continue-btn"
@click="step = 4"
class="o2-primary-button tw-h-[36px] q-ml-md"
class="o2-primary-button tw:h-[36px] q-ml-md"
:class="store.state.theme === 'dark' ? 'o2-primary-button-dark' : 'o2-primary-button-light'"
flat
:label="'Continue'"
@@ -517,7 +517,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
data-test="add-action-script-step4-back-btn"
flat
@click="step = 3"
class="o2-secondary-button tw-h-[36px]"
class="o2-secondary-button tw:h-[36px]"
:label="'Back'"
no-caps
/>
@@ -528,14 +528,14 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
</div>
</div>
</div>
<div class="tw-mx-2">
<div class="tw:mx-2">
<div
class="flex justify-end q-px-md full-width tw-py-[0.625rem] card-container"
class="flex justify-end q-px-md full-width tw:py-[0.625rem] card-container"
style="position: sticky; bottom: 0px; z-index: 2"
>
<q-btn
data-test="add-action-script-cancel-btn"
class="q-mr-md o2-secondary-button tw-h-[36px]"
class="q-mr-md o2-secondary-button tw:h-[36px]"
flat
:label="t('alerts.cancel')"
no-caps
@@ -544,7 +544,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<q-btn
data-test="add-action-script-save-btn"
:label="t('alerts.save')"
class="o2-primary-button tw-h-[36px]"
class="o2-primary-button tw:h-[36px]"
flat
no-caps
@click="saveActionScript"
@@ -47,14 +47,14 @@ describe("FileItem.vue", () => {
wrapper = createWrapper({ isActive: true });
const listItem = wrapper.find("li");
expect(listItem.classes()).toContain("bg-primary");
expect(listItem.classes()).toContain("tw-text-white");
expect(listItem.classes()).toContain("tw:text-white");
});
it("does not apply active styling when isActive is false", () => {
wrapper = createWrapper({ isActive: false });
const listItem = wrapper.find("li");
expect(listItem.classes()).not.toContain("bg-primary");
expect(listItem.classes()).not.toContain("tw-text-white");
expect(listItem.classes()).not.toContain("tw:text-white");
});
it("renders edit and delete buttons", () => {
@@ -264,8 +264,8 @@ describe("FileItem.vue", () => {
await nextTick();
const input = wrapper.find("input");
expect(input.classes()).toContain("tw-text-white");
expect(input.classes()).not.toContain("tw-text-black");
expect(input.classes()).toContain("tw:text-white");
expect(input.classes()).not.toContain("tw:text-black");
});
it("applies black text when not active and editing", async () => {
@@ -276,8 +276,8 @@ describe("FileItem.vue", () => {
await nextTick();
const input = wrapper.find("input");
expect(input.classes()).toContain("tw-text-black");
expect(input.classes()).not.toContain("tw-text-white");
expect(input.classes()).toContain("tw:text-black");
expect(input.classes()).not.toContain("tw:text-white");
});
});
@@ -287,8 +287,8 @@ describe("FileItem.vue", () => {
const buttons = wrapper.findAll("button");
buttons.forEach((button) => {
expect(button.classes()).toContain("tw-text-gray-100");
expect(button.classes()).not.toContain("tw-text-gray-600");
expect(button.classes()).toContain("tw:text-gray-100");
expect(button.classes()).not.toContain("tw:text-gray-600");
});
});
@@ -297,8 +297,8 @@ describe("FileItem.vue", () => {
const buttons = wrapper.findAll("button");
buttons.forEach((button) => {
expect(button.classes()).toContain("tw-text-gray-600");
expect(button.classes()).not.toContain("tw-text-gray-100");
expect(button.classes()).toContain("tw:text-gray-600");
expect(button.classes()).not.toContain("tw:text-gray-100");
});
});
});
@@ -377,10 +377,10 @@ describe("FileItem.vue", () => {
wrapper = createWrapper();
const listItem = wrapper.find("li");
expect(listItem.classes()).toContain("tw-cursor-pointer");
expect(listItem.classes()).toContain("tw-py-[1px]");
expect(listItem.classes()).toContain("tw-px-2");
expect(listItem.classes()).toContain("hover:tw-bg-gray-200");
expect(listItem.classes()).toContain("tw:cursor-pointer");
expect(listItem.classes()).toContain("tw:py-[1px]");
expect(listItem.classes()).toContain("tw:px-2");
expect(listItem.classes()).toContain("hover:tw:bg-gray-200");
expect(listItem.classes()).toContain("file-item");
});
@@ -1,18 +1,18 @@
<template>
<li
class="tw-cursor-pointer tw-py-[1px] tw-px-2 hover:tw-bg-gray-200 tw-text-[14px] tw-rounded-[2px] tw-flex tw-h-[25px] file-item"
class="tw:cursor-pointer tw:py-[1px] tw:px-2 hover:tw:bg-gray-200 tw:text-[14px] tw:rounded-[2px] tw:flex tw:h-[25px] file-item"
:class="{
'bg-primary tw-text-white': isActive,
'bg-primary tw:text-white': isActive,
}"
@click="openFile"
>
<div class="tw-w-[calc(100%-40px)] tw-flex tw-items-center">
<div class="tw:w-[calc(100%-40px)] tw:flex tw:items-center">
<template v-if="isEditing">
<input
ref="nameInput"
v-model.trim="fileName"
class="tw-w-full tw-border tw-rounded tw-h-full tw-bg-transparent tw-border-none tw-outline-none"
:class="isActive ? 'tw-text-white' : 'tw-text-black'"
class="tw:w-full tw:border tw:rounded tw:h-full tw:bg-transparent tw:border-none tw:outline-none"
:class="isActive ? 'tw:text-white' : 'tw:text-black'"
@blur="onBlur"
/>
</template>
@@ -21,17 +21,17 @@
</template>
</div>
<div
class="tw-w-[36px] tw-flex tw-items-center tw-space-x-2 tw-ml-auto file-actions"
class="tw:w-[36px] tw:flex tw:items-center tw:space-x-2 tw:ml-auto file-actions"
>
<button
@click.stop="editFile"
:class="isActive ? 'tw-text-gray-100' : 'tw-text-gray-600'"
:class="isActive ? 'tw:text-gray-100' : 'tw:text-gray-600'"
>
<q-icon name="edit" />
</button>
<button
@click.stop="deleteFile"
:class="isActive ? 'tw-text-gray-100' : 'tw-text-gray-600'"
:class="isActive ? 'tw:text-gray-100' : 'tw:text-gray-600'"
>
<q-icon name="delete" />
</button>
@@ -308,22 +308,22 @@ describe('ScriptEditor.vue', () => {
describe('Component Structure', () => {
it('has correct main wrapper structure', () => {
wrapper = createWrapper();
const mainDiv = wrapper.find('.tw-w-full.tw-h-full');
const mainDiv = wrapper.find('.tw\\:w-full.tw\\:h-full');
expect(mainDiv.exists()).toBe(true);
});
it('has correct editor section structure', () => {
wrapper = createWrapper();
const editorSection = wrapper.find('[data-test="test-function-input-editor-section"]');
expect(editorSection.exists()).toBe(true);
expect(editorSection.classes()).toContain('tw-border-[1px]');
expect(editorSection.classes()).toContain('tw-border-gray-200');
expect(editorSection.classes()).toContain('tw-h-[calc(100%-30px)]');
expect(editorSection.classes()).toContain('tw-relative');
expect(editorSection.classes()).toContain('tw-rounded-md');
expect(editorSection.classes()).toContain('tw-overflow-hidden');
expect(editorSection.classes()).toContain('tw:border-[1px]');
expect(editorSection.classes()).toContain('tw:border-gray-200');
expect(editorSection.classes()).toContain('tw:h-[calc(100%-30px)]');
expect(editorSection.classes()).toContain('tw:relative');
expect(editorSection.classes()).toContain('tw:rounded-md');
expect(editorSection.classes()).toContain('tw:overflow-hidden');
});
it('query editor has correct CSS classes', async () => {
@@ -1,5 +1,5 @@
<template>
<div class="tw-w-full tw-h-full">
<div class="tw:w-full tw:h-full">
<FullViewContainer
data-test="test-function-input-title-section"
:name="file.name"
@@ -9,24 +9,24 @@
<template #left>
<div
v-if="loading"
class="text-weight-bold tw-flex tw-items-center tw-text-gray-500 tw-ml-2 tw-text-[13px]"
class="text-weight-bold tw:flex tw:items-center tw:text-gray-500 tw:ml-2 tw:text-[13px]"
>
<q-spinner-hourglass size="18px" />
<div class="tw-relative tw-top-[2px]">
<div class="tw:relative tw:top-[2px]">
{{ t("confirmDialog.loading") }}
</div>
</div>
<q-icon
v-if="!!error"
name="info"
class="tw-text-red-600 tw-mx-1 tw-cursor-pointer"
class="tw:text-red-600 tw:mx-1 tw:cursor-pointer"
size="16px"
>
<q-tooltip
anchor="center right"
self="center left"
:offset="[10, 10]"
class="tw-text-[12px]"
class="tw:text-[12px]"
>
{{ error }}
</q-tooltip>
@@ -35,14 +35,14 @@
</FullViewContainer>
<div
v-show="isExpanded"
class="tw-border-[1px] tw-border-gray-200 tw-h-[calc(100%-30px)] tw-relative tw-rounded-md tw-overflow-hidden"
class="tw:border-[1px] tw:border-gray-200 tw:h-[calc(100%-30px)] tw:relative tw:rounded-md tw:overflow-hidden"
data-test="test-function-input-editor-section"
>
<query-editor
data-test="vrl-function-test-events-editor"
ref="eventsEditorRef"
:editor-id="`test-function-events-input-editor-${file.name}`"
class="monaco-editor test-function-input-editor tw-h-full"
class="monaco-editor test-function-input-editor tw:h-full"
v-model:query="inputScript"
:language="file.language"
/>
@@ -356,17 +356,17 @@ describe('ScriptToolbar.vue', () => {
const toolbar = wrapper.find('.action-scripts-toolbar');
expect(toolbar.exists()).toBe(true);
expect(toolbar.classes()).toContain('tw-pb-1.5');
expect(toolbar.classes()).toContain('tw-w-full');
expect(toolbar.classes()).toContain('tw-flex');
expect(toolbar.classes()).toContain('tw-justify-between');
expect(toolbar.classes()).toContain('tw-items-center');
expect(toolbar.classes()).toContain('tw:pb-1.5');
expect(toolbar.classes()).toContain('tw:w-full');
expect(toolbar.classes()).toContain('tw:flex');
expect(toolbar.classes()).toContain('tw:justify-between');
expect(toolbar.classes()).toContain('tw:items-center');
});
it('has correct left section structure', () => {
wrapper = createWrapper();
const leftSection = wrapper.find('.tw-flex.tw-items-center');
const leftSection = wrapper.find('.tw\\:flex.tw\\:items-center');
expect(leftSection.exists()).toBe(true);
});
@@ -1,9 +1,9 @@
<template>
<div
class="action-scripts-toolbar tw-pb-1.5 tw-w-full tw-flex tw-justify-between tw-items-center"
class="action-scripts-toolbar tw:pb-1.5 tw:w-full tw:flex tw:justify-between tw:items-center"
>
<div class="tw-flex tw-items-center">
<div class="tw-mr-2 add-script-back-btn">
<div class="tw:flex tw:items-center">
<div class="tw:mr-2 add-script-back-btn">
<div
data-test="add-script-back-btn"
class="flex justify-center items-center cursor-pointer"
@@ -19,18 +19,18 @@
<q-icon name="arrow_back_ios_new" size="14px" />
</div>
</div>
<div class="tw-text-lg tw-w-full add-script-title q-mr-md">
<div class="tw:text-lg tw:w-full add-script-title q-mr-md">
Add Action
</div>
<q-form ref="addScriptForm" class="o2-input">
<div class="tw-flex tw-items-center">
<div class="tw:flex tw:items-center">
<q-input
data-test="add-script-name-input"
v-model.trim="actionName"
:label="t('actions.name')"
color="input-border"
bg-color="input-bg"
class="q-pa-none tw-w-full"
class="q-pa-none tw:w-full"
stack-label
outlined
filled
@@ -60,7 +60,7 @@
self="center left"
max-width="300px"
:offset="[2, 0]"
class="tw-text-[12px]"
class="tw:text-[12px]"
>
{{ isValidMethodName() }}
</q-tooltip>
@@ -72,7 +72,7 @@
<q-btn
data-test="add-script-fullscreen-btn"
v-close-popup="true"
class="text-bold tw-border-primary add-script-fullscreen-btn"
class="text-bold tw:border-primary add-script-fullscreen-btn"
:label="t('common.fullscreen')"
:text-color="store.state.theme === 'dark' ? 'grey-1' : 'primary'"
padding="sm"
@@ -83,7 +83,7 @@
<q-btn
data-test="add-script-save-btn"
:label="t('actions.save')"
class="text-bold no-border tw-ml-[12px] add-script-save-btn"
class="text-bold no-border tw:ml-[12px] add-script-save-btn"
color="secondary"
padding="sm md"
type="submit"
@@ -92,7 +92,7 @@
/>
<q-btn
data-test="add-script-cancel-btn"
class="cancel-btn text-bold tw-ml-[12px] tw-border-3 tw-border-red-600 add-script-cancel-btn"
class="cancel-btn text-bold tw:ml-[12px] tw:border-3 tw:border-red-600 add-script-cancel-btn"
:label="t('common.cancel')"
text-color="negative"
padding="sm md"
+38 -38
View File
@@ -16,8 +16,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<template>
<div class="full-width q-mx-lg q-pt-xs">
<div class="row items-center no-wrap card-container tw-mx-[0.625rem] tw-mb-[0.625rem]">
<div class="flex items-center justify-between tw-w-full card-container tw-h-[68px] tw-px-2 tw-py-3">
<div class="row items-center no-wrap card-container tw:mx-[0.625rem] tw:mb-[0.625rem]">
<div class="flex items-center justify-between tw:w-full card-container tw:h-[68px] tw:px-2 tw:py-3">
<div class="flex items-center">
<div
data-test="add-alert-back-btn"
@@ -40,7 +40,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
{{ t("alerts.addTitle") }}
</div>
</div>
<div class="flex items-center tw-gap-2">
<div class="flex items-center tw:gap-2">
<q-btn
outline
class="pipeline-icons q-px-sm hideOnPrintMode"
@@ -58,14 +58,14 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<!-- WIZARD VIEW -->
<div
class="wizard-view-container tw-mb-2"
class="wizard-view-container tw:mb-2"
style="
max-height: calc(100vh - 194px);
overflow-y: auto;
scroll-behavior: smooth;
"
>
<div class="card-container tw-px-2 tw-mx-[0.675rem] tw-py-2">
<div class="card-container tw:px-2 tw:mx-[0.675rem] tw:py-2">
<!-- Stepper Header (Full Width) -->
<q-form class="add-alert-form" ref="addAlertForm" @submit="onSubmit">
<q-stepper
@@ -81,7 +81,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<template v-slot:message>
<div
v-if="currentStepCaption"
class="persistent-step-caption tw-px-3 tw-py-1 tw-mb-1 tw-mt-2"
class="persistent-step-caption tw:px-3 tw:py-1 tw:mb-1 tw:mt-2"
:class="store.state.theme === 'dark' ? 'dark-mode-caption' : 'light-mode-caption'"
>
{{ currentStepCaption }}
@@ -98,10 +98,10 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
:disable="1 > lastValidStep"
>
<!-- 60/40 Split Layout with Equal Heights -->
<div class="tw-flex tw-gap-[0.625rem] tw-items-stretch" style="height: calc(100vh - 302px); overflow-x: hidden;">
<div class="tw:flex tw:gap-[0.625rem] tw:items-stretch" style="height: calc(100vh - 302px); overflow-x: hidden;">
<!-- Left Column: Step Content (60%) -->
<div class="tw-flex-[0_0_60%] tw-flex tw-flex-col" style="height: calc(100vh - 302px); overflow: hidden;">
<div class="tw-flex-1" style="overflow: auto;">
<div class="tw:flex-[0_0_60%] tw:flex tw:flex-col" style="height: calc(100vh - 302px); overflow: hidden;">
<div class="tw:flex-1" style="overflow: auto;">
<AlertSetup
ref="step1Ref"
:formData="formData"
@@ -121,7 +121,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
</div>
<div class="tw-flex-1">
<div class="tw:flex-1">
<AlertWizardRightColumn
ref="previewAlertRef"
:formData="formData"
@@ -148,10 +148,10 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
:disable="2 > lastValidStep"
>
<!-- 60/40 Split Layout with Equal Heights -->
<div class="tw-flex tw-gap-[0.625rem] tw-items-stretch" style="height: calc(100vh - 302px); overflow-x: hidden;">
<div class="tw:flex tw:gap-[0.625rem] tw:items-stretch" style="height: calc(100vh - 302px); overflow-x: hidden;">
<!-- Left Column: Step Content (60%) -->
<div class="tw-flex-[0_0_60%] tw-flex tw-flex-col" style="height: calc(100vh - 302px); overflow: hidden;">
<div class="tw-flex-1" style="overflow: auto;">
<div class="tw:flex-[0_0_60%] tw:flex tw:flex-col" style="height: calc(100vh - 302px); overflow: hidden;">
<div class="tw:flex-1" style="overflow: auto;">
<QueryConfig
ref="step2Ref"
:tab="formData.query_condition.type || 'custom'"
@@ -181,7 +181,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
</div>
<div class="tw-flex-1">
<div class="tw:flex-1">
<AlertWizardRightColumn
ref="previewAlertRef"
:formData="formData"
@@ -209,10 +209,10 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
:disable="3 > lastValidStep"
>
<!-- 60/40 Split Layout with Equal Heights -->
<div class="tw-flex tw-gap-[0.625rem] tw-items-stretch" style="height: calc(100vh - 302px); overflow-x: hidden;">
<div class="tw:flex tw:gap-[0.625rem] tw:items-stretch" style="height: calc(100vh - 302px); overflow-x: hidden;">
<!-- Left Column: Step Content (60%) -->
<div class="tw-flex-[0_0_60%] tw-flex tw-flex-col" style="height: 100%; overflow: hidden;">
<div class="tw-flex-1" style="overflow: auto;">
<div class="tw:flex-[0_0_60%] tw:flex tw:flex-col" style="height: 100%; overflow: hidden;">
<div class="tw:flex-1" style="overflow: auto;">
<CompareWithPast
ref="step3Ref"
:multiTimeRange="formData.query_condition.multi_time_range"
@@ -228,7 +228,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
</div>
<div class="tw-flex-1">
<div class="tw:flex-1">
<AlertWizardRightColumn
ref="previewAlertRef"
:formData="formData"
@@ -255,10 +255,10 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
:disable="4 > lastValidStep"
>
<!-- 60/40 Split Layout with Equal Heights -->
<div class="tw-flex tw-gap-[0.625rem] tw-items-stretch" style="height: calc(100vh - 302px); overflow-x: hidden;">
<div class="tw:flex tw:gap-[0.625rem] tw:items-stretch" style="height: calc(100vh - 302px); overflow-x: hidden;">
<!-- Left Column: Step Content (60%) -->
<div class="tw-flex-[0_0_60%] tw-flex tw-flex-col" style="height: calc(100vh - 302px); overflow: hidden;">
<div class="tw-flex-1" style="overflow: auto;">
<div class="tw:flex-[0_0_60%] tw:flex tw:flex-col" style="height: calc(100vh - 302px); overflow: hidden;">
<div class="tw:flex-1" style="overflow: auto;">
<AlertSettings
ref="step4Ref"
:formData="formData"
@@ -277,7 +277,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
</div>
<div class="tw-flex-1">
<div class="tw:flex-1">
<AlertWizardRightColumn
ref="previewAlertRef"
:formData="formData"
@@ -305,10 +305,10 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
:disable="5 > lastValidStep"
>
<!-- 60/40 Split Layout with Equal Heights -->
<div class="tw-flex tw-gap-[0.625rem] tw-items-stretch" style="height: calc(100vh - 302px); overflow-x: hidden;">
<div class="tw:flex tw:gap-[0.625rem] tw:items-stretch" style="height: calc(100vh - 302px); overflow-x: hidden;">
<!-- Left Column: Step Content (60%) -->
<div class="tw-flex-[0_0_60%] tw-flex tw-flex-col" style="height: calc(100vh - 302px); overflow: hidden;">
<div class="tw-flex-1" style="overflow: auto;">
<div class="tw:flex-[0_0_60%] tw:flex tw:flex-col" style="height: calc(100vh - 302px); overflow: hidden;">
<div class="tw:flex-1" style="overflow: auto;">
<Deduplication
:deduplication="formData.deduplication"
:columns="filteredColumns"
@@ -318,7 +318,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
</div>
<div class="tw-flex-1">
<div class="tw:flex-1">
<AlertWizardRightColumn
ref="previewAlertRef"
:formData="formData"
@@ -345,10 +345,10 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
:disable="6 > lastValidStep"
>
<!-- 60/40 Split Layout with Equal Heights -->
<div class="tw-flex tw-gap-[0.625rem] tw-items-stretch" style="height: calc(100vh - 302px); overflow-x: hidden;">
<div class="tw:flex tw:gap-[0.625rem] tw:items-stretch" style="height: calc(100vh - 302px); overflow-x: hidden;">
<!-- Left Column: Step Content (60%) -->
<div class="tw-flex-[0_0_60%] tw-flex tw-flex-col" style="height: 100%; overflow: hidden;">
<div class="tw-flex-1" style="overflow: auto;">
<div class="tw:flex-[0_0_60%] tw:flex tw:flex-col" style="height: 100%; overflow: hidden;">
<div class="tw:flex-1" style="overflow: auto;">
<Advanced
:contextAttributes="formData.context_attributes"
:description="formData.description"
@@ -363,7 +363,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
</div>
<div class="tw-flex-1">
<div class="tw:flex-1">
<AlertWizardRightColumn
ref="previewAlertRef"
:formData="formData"
@@ -383,19 +383,19 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
</q-form>
</div>
</div>
<div class="tw-mx-2">
<div class="tw:mx-2">
<div
class="flex q-px-md full-width tw-py-3 card-container tw-justify-end"
class="flex q-px-md full-width tw:py-3 card-container tw:justify-end"
style="position: sticky; bottom: 0px; z-index: 2"
>
<!-- All Buttons (Right Side) -->
<div class="tw-flex tw-items-center tw-gap-2">
<div class="tw:flex tw:items-center tw:gap-2">
<!-- Wizard Navigation Buttons -->
<q-btn
flat
label="Back"
icon="arrow_back"
class="o2-secondary-button tw-h-[36px]"
class="o2-secondary-button tw:h-[36px]"
:class="store.state.theme === 'dark' ? 'o2-secondary-button-dark' : 'o2-secondary-button-light'"
:disable="wizardStep === 1"
no-caps
@@ -405,19 +405,19 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
flat
label="Continue"
icon-right="arrow_forward"
class="o2-secondary-button tw-h-[36px]"
class="o2-secondary-button tw:h-[36px]"
:class="store.state.theme === 'dark' ? 'o2-secondary-button-dark' : 'o2-secondary-button-light'"
:disable="isLastStep"
no-caps
@click="goToNextStep"
/>
<q-separator vertical class="tw-mx-2" style="height: 36px;" />
<q-separator vertical class="tw:mx-2" style="height: 36px;" />
<!-- Cancel and Save Buttons -->
<q-btn
data-test="add-alert-cancel-btn"
v-close-popup="true"
class="o2-secondary-button tw-h-[36px]"
class="o2-secondary-button tw:h-[36px]"
:label="t('alerts.cancel')"
no-caps
flat
@@ -426,7 +426,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
/>
<q-btn
data-test="add-alert-submit-btn"
class="o2-primary-button no-border tw-h-[36px]"
class="o2-primary-button no-border tw:h-[36px]"
:label="t('alerts.save')"
type="submit"
no-caps
+9 -9
View File
@@ -16,14 +16,14 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<template>
<q-page class="q-pa-none o2-custom-bg" style="height: calc(100vh - 48px); min-height: inherit" >
<div class="row items-center no-wrap card-container q-px-md tw-mb-[0.675rem]">
<div class="flex items-center tw-h-[60px]">
<div class="row items-center no-wrap card-container q-px-md tw:mb-[0.675rem]">
<div class="flex items-center tw:h-[60px]">
<div
no-caps
padding="xs"
outline
icon="arrow_back_ios_new"
class="el-border tw-w-6 tw-h-6 flex items-center justify-center cursor-pointer el-border-radius q-mr-sm"
class="el-border tw:w-6 tw:h-6 flex items-center justify-center cursor-pointer el-border-radius q-mr-sm"
title="Go Back"
@click="$emit('cancel:hideform')"
>
@@ -39,11 +39,11 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
</div>
</div>
</div>
<div class="card-container tw-h-full tw-py-2">
<div class="card-container tw:h-full tw:py-2">
<div>
<div class="row q-col-gutter-sm q-px-md q-mt-sm q-mb-xs">
<div v-if="isAlerts" class="col-12 q-pb-md">
<div class="app-tabs-container tw-h-[36px] q-mr-sm tw-w-fit">
<div class="app-tabs-container tw:h-[36px] q-mr-sm tw:w-fit">
<app-tabs
data-test="add-destination-tabs"
:tabs="tabs"
@@ -231,7 +231,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<div class="col-12 q-py-sm">
<q-toggle
data-test="add-destination-skip-tls-verify-toggle"
class="o2-toggle-button-lg tw-mr-3 -tw-ml-4"
class="o2-toggle-button-lg tw:mr-3 -tw:ml-4"
size="lg"
v-model="formData.skip_tls_verify"
:label="t('alert_destinations.skip_tls_verify')"
@@ -281,11 +281,11 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
</template>
</div>
</div>
<div class="flex justify-end q-px-lg q-py-lg full-width tw-absolute tw-bottom-0">
<div class="flex justify-end q-px-lg q-py-lg full-width tw:absolute tw:bottom-0">
<q-btn
data-test="add-destination-cancel-btn"
v-close-popup="true"
class="q-mr-md o2-secondary-button tw-h-[36px]"
class="q-mr-md o2-secondary-button tw:h-[36px]"
:label="t('alerts.cancel')"
no-caps
flat
@@ -293,7 +293,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
/>
<q-btn
data-test="add-destination-submit-btn"
class="o2-primary-button no-border tw-h-[36px]"
class="o2-primary-button no-border tw:h-[36px]"
:label="t('alerts.save')"
type="submit"
no-caps
+10 -10
View File
@@ -19,15 +19,15 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
style="height: calc(100vh - 48px); min-height: inherit"
>
<div
class="row items-center no-wrap card-container q-px-md tw-mb-[0.675rem]"
class="row items-center no-wrap card-container q-px-md tw:mb-[0.675rem]"
>
<div class="flex items-center tw-h-[60px]">
<div class="flex items-center tw:h-[60px]">
<div
no-caps
padding="xs"
outline
icon="arrow_back_ios_new"
class="el-border tw-w-6 tw-h-6 flex items-center justify-center cursor-pointer el-border-radius q-mr-sm"
class="el-border tw:w-6 tw:h-6 flex items-center justify-center cursor-pointer el-border-radius q-mr-sm"
title="Go Back"
@click="$emit('cancel:hideform')"
>
@@ -50,8 +50,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
style="height: calc(100vh - 106px)"
>
<template v-slot:before>
<div class="card-container tw-h-full tw-flex tw-flex-col">
<div class="q-pa-md tw-flex-1 tw-overflow-auto">
<div class="card-container tw:h-full tw:flex tw:flex-col">
<div class="q-pa-md tw:flex-1 tw:overflow-auto">
<div class="col-12 q-pb-sm q-pt-sm o2-input">
<q-input
data-test="add-template-name-input"
@@ -74,7 +74,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
/>
</div>
<div class="col-12 q-pb-md">
<div class="app-tabs-container tw-w-fit">
<div class="app-tabs-container tw:w-fit">
<app-tabs
class="tabs-selection-container"
:tabs="tabs"
@@ -125,11 +125,11 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
</div>
</div>
<div
class="flex justify-end q-px-lg q-py-lg full-width tw-bg-[var(--q-card-background)]"
class="flex justify-end q-px-lg q-py-lg full-width tw:bg-[var(--q-card-background)]"
>
<q-btn
v-close-popup
class="q-mr-md o2-secondary-button tw-h-[36px]"
class="q-mr-md o2-secondary-button tw:h-[36px]"
:label="t('alerts.cancel')"
no-caps
flat
@@ -138,7 +138,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
data-test="add-template-cancel-btn"
/>
<q-btn
class="o2-primary-button no-border tw-h-[36px]"
class="o2-primary-button no-border tw:h-[36px]"
:label="t('alerts.save')"
no-caps
flat
@@ -151,7 +151,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
</template>
<template v-slot:after>
<div
class="q-px-sm q-pt-sm tw-h-full tw-overflow-auto card-container"
class="q-px-sm q-pt-sm tw:h-full tw:overflow-auto card-container"
>
<div class="text-bold q-py-sm q-px-xs text-subtitle2">
{{ t("alert_templates.variable_guide_header") }}
+16 -16
View File
@@ -19,10 +19,10 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
data-test="alert-history-page"
class="q-pa-none flex"
>
<div class="tw-w-full tw-h-full tw-px-[0.625rem] tw-pt-[0.325rem]">
<div class="card-container tw-mb-[0.625rem]">
<div class="tw:w-full tw:h-full tw:px-[0.625rem] tw:pt-[0.325rem]">
<div class="card-container tw:mb-[0.625rem]">
<div
class="flex justify-between full-width tw-h-[68px] tw-px-2 tw-py-3"
class="flex justify-between full-width tw:h-[68px] tw:px-2 tw:py-3"
>
<div class="flex items-center">
<q-btn
@@ -35,7 +35,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
data-test="alert-history-back-btn"
/>
<div
class="q-table__title tw-font-[600] q-ml-sm"
class="q-table__title tw:font-[600] q-ml-sm"
data-test="alerts-history-title"
>
{{ t(`alerts.history`) }}
@@ -119,8 +119,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
</div>
</div>
</div>
<div class="tw-w-full tw-h-full tw-px-[0.625rem]">
<div class="alert-history-table card-container tw-h-[calc(100vh-130px)]">
<div class="tw:w-full tw:h-full tw:px-[0.625rem]">
<div class="alert-history-table card-container tw:h-[calc(100vh-130px)]">
<q-table
data-test="alert-history-table"
ref="qTable"
@@ -136,7 +136,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
style="width: 100%; height: calc(100vh - 130px)"
>
<template #no-data>
<div class="tw-h-[100vh] full-width">
<div class="tw:h-[100vh] full-width">
<no-data />
</div>
</template>
@@ -300,8 +300,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
</template>
<template #bottom="scope">
<div class="bottom-btn tw-h-[48px] tw-w-full tw-flex tw-items-center">
<div class="o2-table-footer-title tw-flex tw-items-center tw-w-[120px] tw-mr-md">
<div class="bottom-btn tw:h-[48px] tw:w-full tw:flex tw:items-center">
<div class="o2-table-footer-title tw:flex tw:items-center tw:w-[120px] tw:mr-md">
{{ pagination.rowsNumber }} {{ t('pipeline.header') }}
</div>
<QTablePagination
@@ -543,14 +543,14 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<q-dialog v-model="errorDialog">
<q-card style="min-width: 500px">
<q-card-section class="pipeline-error-header row items-center q-pb-none">
<div class="tw-flex-1">
<div class="tw-flex tw-items-center tw-gap-3 tw-mb-1">
<div class="tw:flex-1">
<div class="tw:flex tw:items-center tw:gap-3 tw:mb-1">
<q-icon name="error" size="24px" class="error-icon" />
<span class="pipeline-name">{{ errorMessage.alert_name }}</span>
</div>
<div class="error-timestamp">
<span class="tw-ml-1">Last error:</span>
<q-icon name="schedule" size="14px" class="tw-mr-1" />
<span class="tw:ml-1">Last error:</span>
<q-icon name="schedule" size="14px" class="tw:mr-1" />
{{ errorMessage.last_error_timestamp && new Date(errorMessage.last_error_timestamp / 1000).toLocaleString() }}
</div>
</div>
@@ -567,8 +567,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<q-separator />
<q-card-section>
<div class="tw-mb-4">
<div class="section-label tw-mb-2">Error Summary</div>
<div class="tw:mb-4">
<div class="section-label tw:mb-2">Error Summary</div>
<div class="error-summary-box">
{{ errorMessage.error }}
</div>
@@ -579,7 +579,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
flat
no-caps
label="Close"
class="o2-secondary-button tw-h-[36px]"
class="o2-secondary-button tw:h-[36px]"
@click="closeErrorDialog"
/>
</q-card-actions>
@@ -24,21 +24,21 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
elevated
class="alert-history-drawer"
>
<div class="tw-h-full tw-flex tw-flex-col">
<div class="tw:h-full tw:flex tw:flex-col">
<!-- Header -->
<div
class="tw-flex tw-items-center tw-justify-between tw-p-4 tw-border-b"
class="tw:flex tw:items-center tw:justify-between tw:p-4 tw:border-b"
:class="
store.state.theme === 'dark'
? 'tw-border-gray-600'
: 'tw-border-gray-200'
? 'tw:border-gray-600'
: 'tw:border-gray-200'
"
>
<div class="tw-flex tw-items-center tw-gap-3">
<div class="tw:flex tw:items-center tw:gap-3">
<q-icon name="history" size="24px" />
<div>
<div class="tw-font-semibold tw-text-lg">{{ props.alertName }}</div>
<div class="tw-text-sm tw-text-gray-500 dark:tw-text-gray-400">
<div class="tw:font-semibold tw:text-lg">{{ props.alertName }}</div>
<div class="tw:text-sm tw:text-gray-500 dark:tw:text-gray-400">
{{ t("alerts.alertHistory") }}
</div>
</div>
@@ -56,43 +56,43 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<!-- Stats Summary -->
<div
v-if="stats"
class="tw-p-4 tw-border-b"
class="tw:p-4 tw:border-b"
:class="
store.state.theme === 'dark'
? 'tw-border-gray-600 tw-bg-gray-800'
: 'tw-border-gray-200 tw-bg-gray-50'
? 'tw:border-gray-600 tw:bg-gray-800'
: 'tw:border-gray-200 tw:bg-gray-50'
"
>
<div class="tw-grid tw-grid-cols-2 tw-gap-4">
<div class="tw:grid tw:grid-cols-2 tw:gap-4">
<div>
<div class="tw-text-xs tw-text-gray-500 dark:tw-text-gray-400">
<div class="tw:text-xs tw:text-gray-500 dark:tw:text-gray-400">
{{ t("alerts.totalEvaluations") }}
</div>
<div class="tw-text-xl tw-font-semibold">
<div class="tw:text-xl tw:font-semibold">
{{ stats.total }}
</div>
</div>
<div>
<div class="tw-text-xs tw-text-gray-500 dark:tw-text-gray-400">
<div class="tw:text-xs tw:text-gray-500 dark:tw:text-gray-400">
{{ t("alerts.firingCount") }}
</div>
<div class="tw-text-xl tw-font-semibold tw-text-red-500">
<div class="tw:text-xl tw:font-semibold tw:text-red-500">
{{ stats.firing }}
</div>
</div>
<div>
<div class="tw-text-xs tw-text-gray-500 dark:tw-text-gray-400">
<div class="tw:text-xs tw:text-gray-500 dark:tw:text-gray-400">
{{ t("alerts.avgEvaluationTime") }}
</div>
<div class="tw-text-lg tw-font-semibold">
<div class="tw:text-lg tw:font-semibold">
{{ formatDuration(stats.avgDuration) }}
</div>
</div>
<div>
<div class="tw-text-xs tw-text-gray-500 dark:tw-text-gray-400">
<div class="tw:text-xs tw:text-gray-500 dark:tw:text-gray-400">
{{ t("alerts.successRate") }}
</div>
<div class="tw-text-lg tw-font-semibold tw-text-green-500">
<div class="tw:text-lg tw:font-semibold tw:text-green-500">
{{ stats.successRate }}%
</div>
</div>
@@ -100,19 +100,19 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
</div>
<!-- Timeline -->
<div class="tw-flex-1 tw-overflow-y-auto tw-p-4">
<div v-if="loading" class="tw-flex tw-justify-center tw-py-8">
<div class="tw:flex-1 tw:overflow-y-auto tw:p-4">
<div v-if="loading" class="tw:flex tw:justify-center tw:py-8">
<q-spinner color="primary" size="40px" />
</div>
<div v-else-if="historyItems.length === 0" class="tw-text-center tw-py-8">
<q-icon name="history" size="48px" class="tw-text-gray-400" />
<div class="tw-mt-2 tw-text-gray-600 dark:tw-text-gray-400">
<div v-else-if="historyItems.length === 0" class="tw:text-center tw:py-8">
<q-icon name="history" size="48px" class="tw:text-gray-400" />
<div class="tw:mt-2 tw:text-gray-600 dark:tw:text-gray-400">
{{ t("alerts.noHistoryData") }}
</div>
</div>
<q-timeline v-else color="primary" class="tw-mt-2">
<q-timeline v-else color="primary" class="tw:mt-2">
<q-timeline-entry
v-for="(item, index) in historyItems"
:key="index"
@@ -120,16 +120,16 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
:icon="getStatusIcon(item.status)"
>
<template #title>
<div class="tw-flex tw-items-center tw-justify-between">
<span class="tw-font-medium">{{ formatStatus(item.status) }}</span>
<span class="tw-text-xs tw-text-gray-500">
<div class="tw:flex tw:items-center tw:justify-between">
<span class="tw:font-medium">{{ formatStatus(item.status) }}</span>
<span class="tw:text-xs tw:text-gray-500">
{{ formatTimestamp(item.timestamp) }}
</span>
</div>
</template>
<template #subtitle>
<div class="tw-text-sm tw-mt-1 tw-space-y-1">
<div class="tw:text-sm tw:mt-1 tw:space-y-1">
<div v-if="item.is_realtime">
<q-badge color="blue" label="Real-time" />
</div>
@@ -137,12 +137,12 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<q-badge color="orange" label="Silenced" />
</div>
<div v-if="item.evaluation_took_in_secs">
<span class="tw-text-gray-600 dark:tw-text-gray-400">
<span class="tw:text-gray-600 dark:tw:text-gray-400">
Duration: {{ formatDuration(item.evaluation_took_in_secs) }}
</span>
</div>
<div v-if="item.error" class="tw-text-red-500 tw-mt-1">
<q-icon name="error" size="14px" class="tw-mr-1" />
<div v-if="item.error" class="tw:text-red-500 tw:mt-1">
<q-icon name="error" size="14px" class="tw:mr-1" />
{{ item.error }}
</div>
</div>
@@ -151,7 +151,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
</q-timeline>
<!-- Load more button -->
<div v-if="hasMore && !loading" class="tw-text-center tw-mt-4">
<div v-if="hasMore && !loading" class="tw:text-center tw:mt-4">
<q-btn
flat
color="primary"
@@ -15,7 +15,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<template>
<div class="alert-history-summary tw-h-full tw-w-full">
<div class="alert-history-summary tw:h-full tw:w-full">
<q-table
data-test="alert-history-summary-table"
:rows="historyRows"
@@ -24,21 +24,21 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
:pagination="pagination"
:loading="loading"
@request="onRequest"
class="tw-h-full"
class="tw:h-full"
flat
bordered
>
<template #body-cell-alert_name="props">
<q-td :props="props" class="cursor-pointer hover:tw-bg-gray-100 dark:hover:tw-bg-gray-700">
<div class="tw-flex tw-items-center" @click="openDrawer(props.row)">
<span class="tw-font-medium">{{ props.row.alert_name }}</span>
<q-td :props="props" class="cursor-pointer hover:tw:bg-gray-100 dark:hover:tw:bg-gray-700">
<div class="tw:flex tw:items-center" @click="openDrawer(props.row)">
<span class="tw:font-medium">{{ props.row.alert_name }}</span>
</div>
</q-td>
</template>
<template #body-cell-current_state="props">
<q-td :props="props">
<div class="tw-flex tw-items-center tw-gap-2">
<div class="tw:flex tw:items-center tw:gap-2">
<q-icon
:name="getStateIcon(props.row.current_state)"
:color="getStateColor(props.row.current_state)"
@@ -56,9 +56,9 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
</template>
<template #no-data>
<div class="tw-w-full tw-text-center tw-py-8">
<q-icon name="history" size="48px" class="tw-text-gray-400" />
<div class="tw-mt-2 tw-text-gray-600 dark:tw-text-gray-400">
<div class="tw:w-full tw:text-center tw:py-8">
<q-icon name="history" size="48px" class="tw:text-gray-400" />
<div class="tw:mt-2 tw:text-gray-600 dark:tw:text-gray-400">
{{ t("alerts.noHistoryData") }}
</div>
</div>
+12 -12
View File
@@ -16,8 +16,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<template>
<div>
<div class="tw-px-[0.625rem] tw-py-[0.1rem]">
<div class="card-container tw-mb-[0.625rem]">
<div class="tw:px-[0.625rem] tw:py-[0.1rem]">
<div class="card-container tw:mb-[0.625rem]">
<!-- Header -->
<div
class="insights-header flex justify-between items-center"
@@ -32,7 +32,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
@click="goBack"
data-test="alert-insights-back-btn"
/>
<div class="q-table__title tw-font-[600] q-ml-sm">{{ t("alerts.insights.title") }}</div>
<div class="q-table__title tw:font-[600] q-ml-sm">{{ t("alerts.insights.title") }}</div>
</div>
<div class="flex items-center">
@@ -92,7 +92,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<!-- Filters Section -->
<div
v-if="show"
class="filters-section tw-flex tw-items-center tw-gap-2 tw-flex-wrap"
class="filters-section tw:flex tw:items-center tw:gap-2 tw:flex-wrap"
>
<span class="filter-label">{{ t("common.filters") }}:</span>
@@ -124,11 +124,11 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<div
v-for="[panelId, filter] in rangeFilters"
:key="panelId"
class="filter-chip tw-rounded tw-flex tw-items-center"
class="filter-chip tw:rounded tw:flex tw:items-center"
:class="
store.state.theme === 'dark'
? 'tw-bg-indigo-900 tw-text-indigo-100'
: 'tw-bg-blue-100 tw-text-blue-800'
? 'tw:bg-indigo-900 tw:text-indigo-100'
: 'tw:bg-blue-100 tw:text-blue-800'
"
data-test="range-filter-chip"
>
@@ -147,7 +147,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
</span>
<q-icon
name="close"
class="chip-close-icon tw-cursor-pointer"
class="chip-close-icon tw:cursor-pointer"
@click="removeRangeFilter(panelId)"
/>
</div>
@@ -171,11 +171,11 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<!-- Action Buttons Row -->
<div
v-if="selectedAlertForAction"
class="action-buttons-row tw-bg-primary tw-bg-opacity-10 tw-flex tw-items-center"
class="action-buttons-row tw:bg-primary tw:bg-opacity-10 tw:flex tw:items-center"
data-test="action-buttons-row"
>
<q-icon name="campaign" color="primary" size="sm" />
<span class="tw-text-sm tw-font-medium"
<span class="tw:text-sm tw:font-medium"
>{{ t("alerts.insights.actions.actionsFor") }} <strong>{{ selectedAlertForAction }}</strong></span
>
@@ -230,8 +230,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
</div>
<!-- Dashboard Content -->
<div class="tw-w-full tw-h-full tw-px-[0.625rem] tw-pb-[0.625rem]">
<div class="card-container tw-mb-[0.625rem] tw-h-[calc(100vh-208px)]">
<div class="tw:w-full tw:h-full tw:px-[0.625rem] tw:pb-[0.625rem]">
<div class="card-container tw:mb-[0.625rem] tw:h-[calc(100vh-208px)]">
<div
@contextmenu="handleNativeContextMenu"
>
@@ -22,7 +22,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
@click.stop
data-test="alert-insights-context-menu"
>
<div class="menu-header tw-px-4 tw-py-2 tw-text-xs tw-font-semibold">
<div class="menu-header tw:px-4 tw:py-2 tw:text-xs tw:font-semibold">
{{ isAlertNameContext ? value : panelTitle }}
</div>
<q-separator />
+60 -60
View File
@@ -21,12 +21,12 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
data-test="alert-list-page"
class="q-pa-none flex flex-col"
>
<div class="tw-w-full tw-h-[68px] tw-px-[0.625rem] tw-mb-[0.625rem] q-pt-xs" v-if="!showAddAlertDialog && !showImportAlertDialog">
<div class="tw:w-full tw:h-[68px] tw:px-[0.625rem] tw:mb-[0.625rem] q-pt-xs" v-if="!showAddAlertDialog && !showImportAlertDialog">
<div class="card-container">
<div
class="flex justify-between full-width tw-py-3 tw-px-4 items-center"
class="flex justify-between full-width tw:py-3 tw:px-4 items-center"
>
<div class="tw-flex tw-items-center tw-gap-4">
<div class="tw:flex tw:items-center tw:gap-4">
<!-- Icon-only View Mode Tabs -->
<div class="view-mode-tabs">
<q-btn
@@ -55,15 +55,15 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<!-- Page Title -->
<div
class="page-title tw-font-[600] tw-text-[20px]"
class="page-title tw:font-[600] tw:text-[20px]"
:data-test="viewMode === 'alerts' ? 'alerts-list-title' : 'incidents-list-title'"
>
{{ viewMode === 'alerts' ? t('alerts.header') : t('alerts.incidents.title') }}
</div>
</div>
<div class="flex q-ml-auto tw-ps-2 items-center">
<div class="flex q-ml-auto tw:ps-2 items-center">
<!-- Tabs only visible in Alerts view -->
<div v-if="viewMode === 'alerts'" class="app-tabs-container tw-h-[36px] q-mr-sm">
<div v-if="viewMode === 'alerts'" class="app-tabs-container tw:h-[36px] q-mr-sm">
<app-tabs
class="tabs-selection-container"
:tabs="alertTabs"
@@ -107,12 +107,12 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
</template>
</q-input>
<!-- All Folders toggle (only for alerts view) -->
<div v-if="viewMode === 'alerts'" class="tw-mb-2">
<div v-if="viewMode === 'alerts'" class="tw:mb-2">
<q-toggle
data-test="alert-list-search-across-folders-toggle"
v-model="searchAcrossFolders"
label="All Folders"
class="tw-mr-3 tw-h-[36px] o2-toggle-button-lg"
class="tw:mr-3 tw:h-[36px] o2-toggle-button-lg"
size="lg"
>
</q-toggle>
@@ -127,7 +127,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
</div>
<q-btn
v-if="false"
class="q-ml-sm o2-secondary-button tw-h-[36px]"
class="q-ml-sm o2-secondary-button tw:h-[36px]"
no-caps
flat
label="Alert Insights"
@@ -138,7 +138,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<!-- Import button (only for alerts view) -->
<q-btn
v-if="viewMode === 'alerts'"
class="q-ml-sm o2-secondary-button tw-h-[36px]"
class="q-ml-sm o2-secondary-button tw:h-[36px]"
no-caps
flat
:label="t(`dashboard.import`)"
@@ -149,7 +149,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<q-btn
v-if="viewMode === 'alerts'"
data-test="alert-list-add-alert-btn"
class="q-ml-sm o2-primary-button tw-h-[36px]"
class="q-ml-sm o2-primary-button tw:h-[36px]"
no-caps
flat
:disable="!destinations.length"
@@ -166,7 +166,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
style="height: calc(100vh - 116px)"
>
<!-- Incidents View (no folders) -->
<div v-if="viewMode === 'incidents'" class="tw-w-full tw-h-full tw-pr-[0.625rem] tw-pb-[0.625rem]">
<div v-if="viewMode === 'incidents'" class="tw:w-full tw:h-full tw:pr-[0.625rem] tw:pb-[0.625rem]">
<IncidentList :searchQuery="incidentSearchQuery" />
</div>
@@ -180,8 +180,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
data-test="alert-list-splitter"
>
<template #before>
<div class="tw-w-full tw-h-full tw-pl-[0.625rem] tw-pb-[0.625rem]">
<div class="tw-h-full">
<div class="tw:w-full tw:h-full tw:pl-[0.625rem] tw:pb-[0.625rem]">
<div class="tw:h-full">
<FolderList
type="alerts"
@update:activeFolderId="updateActiveFolderId"
@@ -190,8 +190,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
</div>
</template>
<template #after>
<div class="tw-w-full tw-h-full tw-pr-[0.625rem] tw-pb-[0.625rem]">
<div class="tw-h-full card-container">
<div class="tw:w-full tw:h-full tw:pr-[0.625rem] tw:pb-[0.625rem]">
<div class="tw:h-full card-container">
<!-- Alert List Table -->
<q-table
v-model:selected="selectedAlerts"
@@ -259,7 +259,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<q-td v-for="col in columns" :key="col.name" :props="props">
<template v-if="col.name === 'name'">
<div class="tw-flex tw-items-center tw-gap-1">
<div class="tw:flex tw:items-center tw:gap-1">
<span>{{ computedName(props.row[col.field]) }}</span>
</div>
<q-tooltip
@@ -302,7 +302,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
</div>
</template>
<template v-else-if="col.name === 'dedup_status'">
<div class="tw-flex tw-items-center tw-justify-center">
<div class="tw:flex tw:items-center tw:justify-center">
<q-icon
v-if="props.row.deduplication?.enabled"
name="check_circle"
@@ -323,7 +323,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
</div>
</template>
<template v-else-if="col.name == 'actions'">
<div class="tw-flex tw-items-center actions-container"
<div class="tw:flex tw:items-center actions-container"
>
<div
data-test="alert-list-loading-alert"
@@ -535,70 +535,70 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
</template> -->
<template #bottom="scope">
<div class="bottom-btn tw-h-[48px]">
<div class="o2-table-footer-title tw-flex tw-items-center tw-w-[200px] tw-mr-md">
<div class="bottom-btn tw:h-[48px]">
<div class="o2-table-footer-title tw:flex tw:items-center tw:w-[200px] tw:mr-md">
{{ resultTotal }} {{ t('alerts.header') }}
</div>
<q-btn
v-if="selectedAlerts.length > 0"
data-test="alert-list-move-across-folders-btn"
class="flex items-center q-mr-sm no-border o2-secondary-button tw-h-[36px]"
class="flex items-center q-mr-sm no-border o2-secondary-button tw:h-[36px]"
:class="store.state.theme === 'dark' ? 'o2-secondary-button-dark' : 'o2-secondary-button-light'"
no-caps
dense
@click="moveMultipleAlerts"
>
<q-icon :name="outlinedDriveFileMove" size="16px" />
<span class="tw-ml-2">Move</span>
<span class="tw:ml-2">Move</span>
</q-btn>
<q-btn
v-if="selectedAlerts.length > 0"
data-test="alert-list-export-alerts-btn"
class="flex items-center q-mr-sm no-border o2-secondary-button tw-h-[36px]"
class="flex items-center q-mr-sm no-border o2-secondary-button tw:h-[36px]"
:class="store.state.theme === 'dark' ? 'o2-secondary-button-dark' : 'o2-secondary-button-light'"
no-caps
dense
@click="multipleExportAlert"
>
<q-icon name="download" size="16px" />
<span class="tw-ml-2">Export</span>
<span class="tw:ml-2">Export</span>
</q-btn>
<q-btn
v-if="selectedAlerts.length > 0"
data-test="alert-list-pause-alerts-btn"
class="flex items-center q-mr-sm no-border o2-secondary-button tw-h-[36px]"
class="flex items-center q-mr-sm no-border o2-secondary-button tw:h-[36px]"
:class="store.state.theme === 'dark' ? 'o2-secondary-button-dark' : 'o2-secondary-button-light'"
no-caps
dense
@click="bulkToggleAlerts('pause')"
>
<q-icon name="pause" size="16px" />
<span class="tw-ml-2">Pause</span>
<span class="tw:ml-2">Pause</span>
</q-btn>
<q-btn
v-if="selectedAlerts.length > 0"
data-test="alert-list-unpause-alerts-btn"
class="tw-flex items-center no-border o2-secondary-button tw-h-[36px] q-mr-sm tw-w-[180px]"
class="tw:flex items-center no-border o2-secondary-button tw:h-[36px] q-mr-sm tw:w-[180px]"
:class="store.state.theme === 'dark' ? 'o2-secondary-button-dark' : 'o2-secondary-button-light'"
no-caps
dense
@click="bulkToggleAlerts('resume')"
>
<q-icon name="play_arrow" size="16px" />
<span class="tw-ml-2">Resume</span>
<span class="tw:ml-2">Resume</span>
</q-btn>
<q-btn
v-if="selectedAlerts.length > 0"
data-test="alert-list-delete-alerts-btn"
class="tw-flex items-center q-mr-sm no-border o2-secondary-button tw-h-[36px] tw-ml-sm"
class="tw:flex items-center q-mr-sm no-border o2-secondary-button tw:h-[36px] tw:ml-sm"
:class="store.state.theme === 'dark' ? 'o2-secondary-button-dark' : 'o2-secondary-button-light'"
no-caps
dense
@click="openBulkDeleteDialog"
>
<q-icon name="delete" size="16px" />
<span class="tw-ml-2">Delete</span>
<span class="tw:ml-2">Delete</span>
</q-btn>
<QTablePagination
:scope="scope"
@@ -662,12 +662,12 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
behavior="mobile"
class="alert-details-drawer"
>
<div class="tw-h-full tw-flex tw-flex-col">
<div class="tw:h-full tw:flex tw:flex-col">
<!-- Drawer Header -->
<div class="tw-px-6 tw-py-4 tw-border-b tw-flex tw-items-center tw-justify-between">
<div class="tw-flex tw-items-center">
<q-icon name="info" size="24px" class="tw-mr-2" />
<h6 class="tw-text-lg tw-font-semibold tw-m-0">{{ t('alert_list.alert_details') }}</h6>
<div class="tw:px-6 tw:py-4 tw:border-b tw:flex tw:items-center tw:justify-between">
<div class="tw:flex tw:items-center">
<q-icon name="info" size="24px" class="tw:mr-2" />
<h6 class="tw:text-lg tw:font-semibold tw:m-0">{{ t('alert_list.alert_details') }}</h6>
</div>
<q-btn
flat
@@ -679,17 +679,17 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
</div>
<!-- Drawer Content -->
<div class="tw-flex-1 tw-overflow-y-auto tw-px-6 tw-py-4" v-if="selectedAlertDetails">
<div class="tw:flex-1 tw:overflow-y-auto tw:px-6 tw:py-4" v-if="selectedAlertDetails">
<!-- Alert Name -->
<div class="tw-mb-6">
<div class="tw-text-sm tw-font-semibold tw-mb-1" :class="store.state.theme === 'dark' ? 'tw-text-gray-400' : 'tw-text-gray-600'">Alert Name</div>
<div class="tw-text-base">{{ selectedAlertDetails.name }}</div>
<div class="tw:mb-6">
<div class="tw:text-sm tw:font-semibold tw:mb-1" :class="store.state.theme === 'dark' ? 'tw:text-gray-400' : 'tw:text-gray-600'">Alert Name</div>
<div class="tw:text-base">{{ selectedAlertDetails.name }}</div>
</div>
<!-- SQL Query / Conditions -->
<div class="tw-mb-6">
<div class="tw-flex tw-items-center tw-justify-between tw-mb-2">
<div class="tw-text-sm tw-font-semibold" :class="store.state.theme === 'dark' ? 'tw-text-gray-400' : 'tw-text-gray-600'">
<div class="tw:mb-6">
<div class="tw:flex tw:items-center tw:justify-between tw:mb-2">
<div class="tw:text-sm tw:font-semibold" :class="store.state.theme === 'dark' ? 'tw:text-gray-400' : 'tw:text-gray-600'">
{{ selectedAlertDetails.type == "sql" ? "SQL Query" : "Conditions" }}
</div>
<q-btn
@@ -699,12 +699,12 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
flat
dense
icon="content_copy"
class="tw-ml-2"
class="tw:ml-2"
>
<q-tooltip>Copy</q-tooltip>
</q-btn>
</div>
<pre :class="store.state.theme === 'dark' ? 'tw-bg-gray-800 tw-text-gray-200' : 'tw-bg-gray-100 tw-text-gray-900'" class="tw-p-3 tw-rounded tw-text-sm tw-overflow-x-auto" style="white-space: pre-wrap">{{
<pre :class="store.state.theme === 'dark' ? 'tw:bg-gray-800 tw:text-gray-200' : 'tw:bg-gray-100 tw:text-gray-900'" class="tw:p-3 tw:rounded tw:text-sm tw:overflow-x-auto" style="white-space: pre-wrap">{{
selectedAlertDetails.conditions != "" && selectedAlertDetails.conditions != "--"
? (selectedAlertDetails.type == 'sql' ? selectedAlertDetails.conditions : selectedAlertDetails.conditions.length != 2 ? `if ${selectedAlertDetails.conditions}` : 'No condition')
: "No condition"
@@ -712,23 +712,23 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
</div>
<!-- Description -->
<div class="tw-mb-6">
<div class="tw-text-sm tw-font-semibold tw-mb-2" :class="store.state.theme === 'dark' ? 'tw-text-gray-400' : 'tw-text-gray-600'">Description</div>
<pre :class="store.state.theme === 'dark' ? 'tw-bg-gray-800 tw-text-gray-200' : 'tw-bg-gray-100 tw-text-gray-900'" class="tw-p-3 tw-rounded tw-text-sm" style="white-space: pre-wrap">{{ selectedAlertDetails.description || "No description" }}</pre>
<div class="tw:mb-6">
<div class="tw:text-sm tw:font-semibold tw:mb-2" :class="store.state.theme === 'dark' ? 'tw:text-gray-400' : 'tw:text-gray-600'">Description</div>
<pre :class="store.state.theme === 'dark' ? 'tw:bg-gray-800 tw:text-gray-200' : 'tw:bg-gray-100 tw:text-gray-900'" class="tw:p-3 tw:rounded tw:text-sm" style="white-space: pre-wrap">{{ selectedAlertDetails.description || "No description" }}</pre>
</div>
<!-- Alert History Table -->
<div class="tw-mb-6">
<div class="tw-text-sm tw-font-semibold tw-mb-3" :class="store.state.theme === 'dark' ? 'tw-text-gray-400' : 'tw-text-gray-600'">Evaluation History</div>
<div class="tw:mb-6">
<div class="tw:text-sm tw:font-semibold tw:mb-3" :class="store.state.theme === 'dark' ? 'tw:text-gray-400' : 'tw:text-gray-600'">Evaluation History</div>
<div v-if="isLoadingHistory" class="tw-text-center tw-py-8">
<div v-if="isLoadingHistory" class="tw:text-center tw:py-8">
<q-spinner size="32px" color="primary" />
<div class="tw-text-sm tw-mt-3" :class="store.state.theme === 'dark' ? 'tw-text-gray-400' : 'tw-text-gray-600'">Loading history...</div>
<div class="tw:text-sm tw:mt-3" :class="store.state.theme === 'dark' ? 'tw:text-gray-400' : 'tw:text-gray-600'">Loading history...</div>
</div>
<div v-else-if="expandedAlertHistory.length === 0" class="tw-text-center tw-py-8" :class="store.state.theme === 'dark' ? 'tw-text-gray-500' : 'tw-text-gray-500'">
<q-icon name="history" size="48px" class="tw-mb-2 tw-opacity-30" />
<div class="tw-text-sm">No evaluation history available for this alert</div>
<div v-else-if="expandedAlertHistory.length === 0" class="tw:text-center tw:py-8" :class="store.state.theme === 'dark' ? 'tw:text-gray-500' : 'tw:text-gray-500'">
<q-icon name="history" size="48px" class="tw:mb-2 tw:opacity-30" />
<div class="tw:text-sm">No evaluation history available for this alert</div>
</div>
<q-table
@@ -739,7 +739,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
flat
dense
:pagination="{ rowsPerPage: 10 }"
class="tw-shadow-sm"
class="tw:shadow-sm"
>
<template v-slot:body-cell-status="props">
<q-td :props="props">
@@ -799,7 +799,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
@update:model-value="updateStreams()"
borderless
dense
class="showLabelOnTop no-case tw-mt-[1px] q-mb-sm"
class="showLabelOnTop no-case tw:mt-[1px] q-mb-sm"
/>
<q-select
data-test="to-be-clone-stream-name"
@@ -816,7 +816,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
:input-debounce="400"
borderless
dense
class="showLabelOnTop no-case tw-mt-[1px] q-mb-sm"
class="showLabelOnTop no-case tw:mt-[1px] q-mb-sm"
/>
<div class="q-mb-lg">
<SelectFolderDropDown
@@ -829,7 +829,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<q-btn
data-test="clone-alert-cancel-btn"
v-close-popup="true"
class="o2-secondary-button tw-h-[36px]"
class="o2-secondary-button tw:h-[36px]"
:label="t('alerts.cancel')"
text-color="light-text"
padding="sm md"
@@ -838,7 +838,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<q-btn
data-test="clone-alert-submit-btn"
:label="t('alerts.save')"
class="o2-primary-button tw-h-[36px] q-ml-md"
class="o2-primary-button tw:h-[36px] q-ml-md"
padding="sm xl"
type="submit"
:disable="isSubmitting"
@@ -16,17 +16,17 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<template>
<!-- Right Column: Preview & Summary (calc to account for gap) -->
<div class="tw-flex-[0_0_calc(32%-0.625rem)] tw-flex tw-flex-col tw-gap-2" style="height: calc(100vh - 302px); position: sticky; top: 0;">
<div class="tw:flex-[0_0_calc(32%-0.625rem)] tw:flex tw:flex-col tw:gap-2" style="height: calc(100vh - 302px); position: sticky; top: 0;">
<!-- Preview Section -->
<div
class="collapsible-section card-container"
:style="previewSectionStyle"
>
<div
class="section-header tw-flex tw-items-center tw-justify-between tw-px-4 tw-py-3 tw-cursor-pointer"
class="section-header tw:flex tw:items-center tw:justify-between tw:px-4 tw:py-3 tw:cursor-pointer"
@click="togglePreview"
>
<span class="tw-text-sm tw-font-semibold">Preview</span>
<span class="tw:text-sm tw:font-semibold">Preview</span>
<q-btn
flat
dense
@@ -56,10 +56,10 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
:style="summarySectionStyle"
>
<div
class="section-header tw-flex tw-items-center tw-justify-between tw-px-4 tw-py-3 tw-cursor-pointer"
class="section-header tw:flex tw:items-center tw:justify-between tw:px-4 tw:py-3 tw:cursor-pointer"
@click="toggleSummary"
>
<span class="tw-text-sm tw-font-semibold">Summary</span>
<span class="tw:text-sm tw:font-semibold">Summary</span>
<q-btn
flat
dense
+14 -14
View File
@@ -1,21 +1,21 @@
<template>
<div
>
<div class="tw-flex tw-justify-between cursor-pointer" @click="expanded = !expanded">
<div class="tw-flex tw-items-start tw-justify-between full-width ">
<div class="tw:flex tw:justify-between cursor-pointer" @click="expanded = !expanded">
<div class="tw:flex tw:items-start tw:justify-between full-width ">
<div
class="tw-text-[16px] tw-flex tw-items-start"
class="tw:text-[16px] tw:flex tw:items-start"
>
<q-icon
v-if="!image"
:name="icon"
size="16px"
class="tw-mr-2 tw-rounded-full tw-px-1 tw-py-1 "
class="tw:mr-2 tw:rounded-full tw:px-1 tw:py-1 "
:class="[
store.state.theme === 'dark'
? 'tw-text-gray-100 tw-bg-gray-600'
? 'tw:text-gray-100 tw:bg-gray-600'
: 'light-mode-icon',
iconClass
]"
@@ -23,21 +23,21 @@
<img
v-else
:src="image"
class="tw-mr-2 tw-rounded-full tw-px-1 tw-py-1"
class="tw:mr-2 tw:rounded-full tw:px-1 tw:py-1"
:class="[
store.state.theme === 'dark'
? 'tw-text-gray-100 tw-bg-gray-600'
? 'tw:text-gray-100 tw:bg-gray-600'
: 'light-mode-icon',
iconClass
]"
/>
<div class="tw-flex tw-flex-col tw-items-start tw-justify-start">
<div class="tw:flex tw:flex-col tw:items-start tw:justify-start">
<span> {{ label }}</span>
<div class="tw-text-[13px] tw-h-[20px]"
<div class="tw:text-[13px] tw:h-[20px]"
:class="[
store.state.theme === 'dark'
? 'tw-text-[#c6c6c6]'
: 'tw-text-gray-900',
? 'tw:text-[#c6c6c6]'
: 'tw:text-gray-900',
]"
>
{{ subLabel }}
@@ -47,12 +47,12 @@
</div>
<q-icon
:name="expanded ? 'keyboard_arrow_up' : 'keyboard_arrow_down'"
class="tw-rounded-full tw-p-1 tw-mt-2"
class="tw:rounded-full tw:p-1 tw:mt-2"
:class="[
store.state.theme === 'dark'
? 'tw-text-gray-100 tw-bg-gray-600'
: 'tw-text-gray-900 tw-bg-gray-300',
? 'tw:text-gray-100 tw:bg-gray-600'
: 'tw:text-gray-900 tw:bg-gray-300',
]"
size="18px"
/>
@@ -18,12 +18,12 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<q-page class="q-pa-none" style="height: calc(100vh - 88px); min-height: inherit" >
<div v-if="!showDestinationEditor && !showImportDestination" >
<div class="tw-flex tw-justify-between tw-items-center tw-px-4 tw-py-3 tw-h-[68px] tw-border-b-[1px]"
<div class="tw:flex tw:justify-between tw:items-center tw:px-4 tw:py-3 tw:h-[68px] tw:border-b-[1px]"
>
<div class="q-table__title tw-font-[600]" data-test="alert-destinations-list-title">
<div class="q-table__title tw:font-[600]" data-test="alert-destinations-list-title">
{{ t("alert_destinations.header") }}
</div>
<div class="tw-flex tw-justify-end">
<div class="tw:flex tw:justify-end">
<q-input
v-model="filterQuery"
borderless
@@ -37,7 +37,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
</template>
</q-input>
<q-btn
class="o2-secondary-button q-ml-sm tw-h-[36px]"
class="o2-secondary-button q-ml-sm tw:h-[36px]"
no-caps
flat
:label="t(`dashboard.import`)"
@@ -46,7 +46,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
/>
<q-btn
data-test="alert-destination-list-add-alert-btn"
class="o2-primary-button q-ml-sm tw-h-[36px]"
class="o2-primary-button q-ml-sm tw:h-[36px]"
no-caps
flat
:disable="!templates.length"
@@ -99,7 +99,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
</template>
<template v-slot:body-cell-actions="props">
<q-td :props="props">
<div class="tw-flex tw-items-center tw-gap-1 tw-justify-center">
<div class="tw:flex tw:items-center tw:gap-1 tw:justify-center">
<q-btn
data-test="destination-export"
padding="sm"
@@ -145,14 +145,14 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
</template>
<template #bottom="scope">
<div class="tw-flex tw-items-center tw-justify-between tw-w-full tw-h-[48px]">
<div class="o2-table-footer-title tw-flex tw-items-center tw-w-[200px] tw-mr-sm">
<div class="tw:flex tw:items-center tw:justify-between tw:w-full tw:h-[48px]">
<div class="o2-table-footer-title tw:flex tw:items-center tw:w-[200px] tw:mr-sm">
{{ resultTotal }} {{ t('alert_destinations.header') }}
</div>
<q-btn
v-if="selectedDestinations.length > 0"
data-test="destination-list-delete-destinations-btn"
class="flex items-center q-mr-sm no-border o2-secondary-button tw-h-[36px]"
class="flex items-center q-mr-sm no-border o2-secondary-button tw:h-[36px]"
:class="
store.state.theme === 'dark'
? 'o2-secondary-button-dark'
@@ -163,7 +163,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
@click="openBulkDeleteDialog"
>
<q-icon name="delete" size="16px" />
<span class="tw-ml-2">Delete</span>
<span class="tw:ml-2">Delete</span>
</q-btn>
<QTablePagination
:scope="scope"
@@ -21,17 +21,17 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
:class="store.state.theme === 'dark' ? 'dark-mode' : 'light-mode'"
>
<!-- Header -->
<q-card-section class="dialog-header tw-pb-3">
<q-card-section class="dialog-header tw:pb-3">
<div class="dialog-title">{{ title }}</div>
</q-card-section>
<!-- Content -->
<q-card-section class="dialog-content tw-py-4">
<q-card-section class="dialog-content tw:py-4">
<div class="message-text">{{ message }}</div>
</q-card-section>
<!-- Actions -->
<q-card-actions class="dialog-actions tw-flex tw-justify-end tw-gap-2 tw-px-4 tw-pb-4">
<q-card-actions class="dialog-actions tw:flex tw:justify-end tw:gap-2 tw:px-4 tw:pb-4">
<q-btn
data-test="custom-cancel-button"
label="Cancel"
+23 -23
View File
@@ -15,25 +15,25 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<template>
<div class="dedup-summary-cards tw-grid tw-grid-cols-4 tw-gap-4 tw-mb-4">
<div class="dedup-summary-cards tw:grid tw:grid-cols-4 tw:gap-4 tw:mb-4">
<!-- Card 1: Total Alerts -->
<q-card class="summary-card">
<q-card-section class="tw-p-4">
<div class="tw-text-2xl tw-font-semibold">{{ summary.total_alerts }}</div>
<div class="tw-text-sm tw-text-gray-600">Total Alerts</div>
<q-card-section class="tw:p-4">
<div class="tw:text-2xl tw:font-semibold">{{ summary.total_alerts }}</div>
<div class="tw:text-sm tw:text-gray-600">Total Alerts</div>
</q-card-section>
</q-card>
<!-- Card 2: Alerts with Dedup -->
<q-card class="summary-card">
<q-card-section class="tw-p-4">
<div class="tw-flex tw-items-center tw-gap-2">
<div class="tw-text-2xl tw-font-semibold">{{ summary.alerts_with_dedup }}</div>
<q-card-section class="tw:p-4">
<div class="tw:flex tw:items-center tw:gap-2">
<div class="tw:text-2xl tw:font-semibold">{{ summary.alerts_with_dedup }}</div>
<q-icon name="filter_alt" size="sm" color="primary" />
</div>
<div class="tw-text-sm tw-text-gray-600 tw-flex tw-items-center tw-gap-1">
<div class="tw:text-sm tw:text-gray-600 tw:flex tw:items-center tw:gap-1">
Using Deduplication
<q-icon name="info_outline" size="xs" class="tw-cursor-pointer">
<q-icon name="info_outline" size="xs" class="tw:cursor-pointer">
<q-tooltip class="bg-grey-8">
Alerts with deduplication configured
</q-tooltip>
@@ -46,24 +46,24 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<q-card
class="summary-card"
:class="{
'tw-bg-green-50': summary.suppression_rate > 0.5,
'tw-bg-yellow-50': summary.suppression_rate > 0 && summary.suppression_rate <= 0.5
'tw:bg-green-50': summary.suppression_rate > 0.5,
'tw:bg-yellow-50': summary.suppression_rate > 0 && summary.suppression_rate <= 0.5
}"
>
<q-card-section class="tw-p-4">
<div class="tw-text-2xl tw-font-semibold" :class="{
'tw-text-green-700': summary.suppression_rate > 0.5,
'tw-text-yellow-700': summary.suppression_rate > 0 && summary.suppression_rate <= 0.5
<q-card-section class="tw:p-4">
<div class="tw:text-2xl tw:font-semibold" :class="{
'tw:text-green-700': summary.suppression_rate > 0.5,
'tw:text-yellow-700': summary.suppression_rate > 0 && summary.suppression_rate <= 0.5
}">
{{ formatPercentage(summary.suppression_rate) }}
</div>
<div class="tw-text-sm tw-text-gray-600 tw-flex tw-items-center tw-gap-1">
<div class="tw:text-sm tw:text-gray-600 tw:flex tw:items-center tw:gap-1">
Suppression Rate (24h)
<q-icon name="info_outline" size="xs" class="tw-cursor-pointer">
<q-icon name="info_outline" size="xs" class="tw:cursor-pointer">
<q-tooltip class="bg-grey-8">
{{ summary.suppressions_total }} suppressed /
{{ summary.suppressions_total + summary.passed_total }} total
<div class="tw-mt-1">
<div class="tw:mt-1">
Passed: {{ summary.passed_total }}
</div>
</q-tooltip>
@@ -74,14 +74,14 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<!-- Card 4: Pending Batches -->
<q-card class="summary-card">
<q-card-section class="tw-p-4">
<div class="tw-flex tw-items-center tw-gap-2">
<div class="tw-text-2xl tw-font-semibold">{{ summary.pending_batches }}</div>
<q-card-section class="tw:p-4">
<div class="tw:flex tw:items-center tw:gap-2">
<div class="tw:text-2xl tw:font-semibold">{{ summary.pending_batches }}</div>
<q-icon name="group_work" size="sm" color="amber" />
</div>
<div class="tw-text-sm tw-text-gray-600 tw-flex tw-items-center tw-gap-1">
<div class="tw:text-sm tw:text-gray-600 tw:flex tw:items-center tw:gap-1">
Pending Batches
<q-icon name="info_outline" size="xs" class="tw-cursor-pointer">
<q-icon name="info_outline" size="xs" class="tw:cursor-pointer">
<q-tooltip class="bg-grey-8">
Alerts waiting to be grouped together
</q-tooltip>
@@ -16,25 +16,25 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<template>
<div class="deduplication-config q-pa-none q-ma-none">
<div class="tw-w-full">
<div class="tw-w-full">
<div class="tw:w-full">
<div class="tw:w-full">
<AlertsContainer
:name="t('alerts.deduplication.title')"
v-model:is-expanded="isExpanded"
:label="t('alerts.deduplication.title')"
:subLabel="t('alerts.deduplication.subtitle')"
icon="filter_alt"
class="tw-w-full col-12 tw-pl-4 tw-pr-2 tw-py-2"
:iconClass="'tw-mt-[2px]'"
class="tw:w-full col-12 tw:pl-4 tw:pr-2 tw:py-2"
:iconClass="'tw:mt-[2px]'"
/>
</div>
<div v-if="isExpanded" class="tw-w-full row alert-setup-container">
<q-separator class="tw-my-2"/>
<div class="q-mt-sm tw-w-full tw-pl-3">
<div v-if="isExpanded" class="tw:w-full row alert-setup-container">
<q-separator class="tw:my-2"/>
<div class="q-mt-sm tw:w-full tw:pl-3">
<!-- Fingerprint Fields -->
<div class="tw-mb-4">
<div class="tw-font-semibold tw-pb-2 tw-flex tw-items-center">
<div class="tw:mb-4">
<div class="tw:font-semibold tw:pb-2 tw:flex tw:items-center">
{{ t("alerts.deduplication.fingerprintFields") }}
<q-icon
:name="outlinedInfo"
@@ -52,7 +52,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
</q-tooltip>
</q-icon>
</div>
<div class="tw-text-sm tw-text-gray-600 dark:tw-text-gray-400 tw-mb-2">
<div class="tw:text-sm tw:text-gray-600 dark:tw:text-gray-400 tw:mb-2">
{{ t("alerts.deduplication.fingerprintFieldsHint") }}
</div>
<q-select
@@ -76,7 +76,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
@update:model-value="emitUpdate"
>
<template v-slot:hint>
<div class="tw-text-xs">
<div class="tw:text-xs">
💡 Leave empty to auto-detect based on query (SQL: GROUP BY columns, PromQL: labels, Custom: condition fields)
</div>
</template>
@@ -84,8 +84,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
</div>
<!-- Time Window -->
<div class="tw-mb-4">
<div class="tw-font-semibold tw-pb-2 tw-flex tw-items-center">
<div class="tw:mb-4">
<div class="tw:font-semibold tw:pb-2 tw:flex tw:items-center">
{{ t("alerts.deduplication.timeWindow") }}
<q-icon
:name="outlinedInfo"
@@ -103,7 +103,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
</q-tooltip>
</q-icon>
</div>
<div class="tw-text-sm tw-text-gray-600 dark:tw-text-gray-400 tw-mb-2">
<div class="tw:text-sm tw:text-gray-600 dark:tw:text-gray-400 tw:mb-2">
{{ t("alerts.deduplication.timeWindowHint") }}
</div>
<q-input
@@ -66,7 +66,7 @@ describe('FilterCondition.vue Branch Coverage', () => {
});
// Branch: index == 0 ? 'if' : computedLabel (line 5)
const labelElement = wrapper.find('.tw-text-sm');
const labelElement = wrapper.find('.tw\\:text-sm');
expect(labelElement.text().trim()).toBe('if');
});
@@ -92,7 +92,7 @@ describe('FilterCondition.vue Branch Coverage', () => {
});
// Branch: computedLabel (when not first in group) - CSS lowercase applied
const labelElement = wrapper.find('.tw-lowercase');
const labelElement = wrapper.find('.tw\\:lowercase');
// Text content is still 'OR' but CSS applies text-transform: lowercase
expect(labelElement.exists()).toBe(true);
expect(labelElement.text().trim()).toBe('OR'); // Content is uppercase, CSS transforms to lowercase visually
@@ -116,7 +116,7 @@ describe('FilterCondition.vue Branch Coverage', () => {
});
// Branch: index == 0 && depth == 0 shows "if"
const labelElement = wrapper.find('.tw-text-sm');
const labelElement = wrapper.find('.tw\\:text-sm');
expect(labelElement.text().trim()).toBe('if');
});
@@ -138,7 +138,7 @@ describe('FilterCondition.vue Branch Coverage', () => {
});
// Branch: isFirstInGroup && depth > 0 shows empty space (no label)
const labelContainer = wrapper.find('.tw-min-w-\\[60px\\]');
const labelContainer = wrapper.find('.tw\\:min-w-\\[60px\\]');
expect(labelContainer.exists()).toBe(true);
// Should not contain operator label or "if"
expect(labelContainer.text().trim()).toBe('');
+11 -11
View File
@@ -1,11 +1,11 @@
<template>
<div class=" tw-flex tw-items-start tw-gap-1 tw-flex-no-wrap ">
<div class=" tw:flex tw:items-start tw:gap-1 tw:flex-no-wrap ">
<!-- V2: Fixed-width left column for alignment -->
<!-- All conditions have the same width for the operator/label section -->
<div class="tw-flex tw-items-center tw-justify-center tw-mt-2 tw-min-w-[60px]">
<div class="tw:flex tw:items-center tw:justify-center tw:mt-2 tw:min-w-[60px]">
<!-- First condition in root group: show "if" centered -->
<template v-if="index === 0 && depth === 0">
<span class="tw-text-sm">if</span>
<span class="tw:text-sm">if</span>
</template>
<!-- First condition in nested groups: empty space for alignment -->
@@ -15,7 +15,7 @@
<!-- Other conditions: show operator + toggle button -->
<template v-else>
<span class="tw-text-sm tw-font-medium tw-min-w-[30px] tw-lowercase">
<span class="tw:text-sm tw:font-medium tw:min-w-[30px] tw:lowercase">
{{ computedLabel }}
</span>
<!-- Toggle AND/OR button after label -->
@@ -26,7 +26,7 @@
round
size="sm"
icon="restart_alt"
class="tw-h-[26px] tw-flex-shrink-0 operator-toggle-btn"
class="tw:h-[26px] tw:flex-shrink-0 operator-toggle-btn"
@click="toggleOperator"
>
<q-tooltip>
@@ -37,7 +37,7 @@
</div>
<div
data-test="alert-conditions-select-column"
class="q-ml-none tw-mb-2"
class="q-ml-none tw:mb-2"
>
<q-select
v-model="condition.column"
@@ -52,7 +52,7 @@
hide-bottom-space
:input-debounce="400"
:placeholder="t('alerts.column')"
class="tw-mb-2"
class="tw:mb-2"
@filter="filterColumns"
behavior="menu"
:rules="[
@@ -81,7 +81,7 @@
dense
hide-bottom-space
:rules="[(val: any) => !!val || 'Field is required!']"
:class="inputWidth ? inputWidth : (store.state.isAiChatEnabled ? 'tw-w-[70px]' : computedInputWidth)"
:class="inputWidth ? inputWidth : (store.state.isAiChatEnabled ? 'tw:w-[70px]' : computedInputWidth)"
@update:model-value="emits('input:update', 'conditions', condition)"
>
<q-tooltip v-if="condition.operator && store.state.isAiChatEnabled">
@@ -103,7 +103,7 @@
dense
hide-bottom-space
:rules="[(val: any) => !!val || 'Field is required!']"
:class="inputWidth ? inputWidth : (store.state.isAiChatEnabled ? 'tw-w-[110px]' : computedValueWidth)"
:class="inputWidth ? inputWidth : (store.state.isAiChatEnabled ? 'tw:w-[110px]' : computedValueWidth)"
@update:model-value="emits('input:update', 'conditions', condition)"
>
<q-tooltip v-if="condition.value && store.state.isAiChatEnabled">
@@ -223,12 +223,12 @@ const toggleOperator = () => {
const computedInputWidth = computed(() => {
// If custom width is provided, use it; otherwise use default responsive width
return props.inputWidth || (store.state.isAiChatEnabled ? '' : 'xl:tw-min-w-[200px] lg:tw-min-w-[90px] lg:tw-w-fit');
return props.inputWidth || (store.state.isAiChatEnabled ? '' : 'xl:tw:min-w-[200px] lg:tw:min-w-[90px] lg:tw:w-fit');
});
const computedValueWidth = computed(() => {
// If custom width is provided, use it; otherwise use default responsive width
return props.inputWidth || (store.state.isAiChatEnabled ? 'tw-w-[110px]' : 'xl:tw-min-w-[200px] lg:tw-w-fit lg:tw-min-w-[80px]');
return props.inputWidth || (store.state.isAiChatEnabled ? 'tw:w-[110px]' : 'xl:tw:min-w-[200px] lg:tw:w-fit lg:tw:min-w-[80px]');
});
@@ -1805,7 +1805,7 @@ describe('FilterGroup.vue Comprehensive Coverage', () => {
const wrapper = mount(FilterGroup, {
props: {
...defaultProps,
conditionInputWidth: 'tw-w-[100px]'
conditionInputWidth: 'tw:w-[100px]'
},
global: {
plugins: [Quasar, mockI18n],
@@ -1818,7 +1818,7 @@ describe('FilterGroup.vue Comprehensive Coverage', () => {
},
});
expect(wrapper.props('conditionInputWidth')).toBe('tw-w-[100px]');
expect(wrapper.props('conditionInputWidth')).toBe('tw:w-[100px]');
});
it('should have empty string as default for conditionInputWidth', () => {
@@ -1860,7 +1860,7 @@ describe('FilterGroup.vue Comprehensive Coverage', () => {
const wrapper = mount(FilterGroup, {
props: {
...defaultProps,
conditionInputWidth: 'tw-w-[100px]'
conditionInputWidth: 'tw:w-[100px]'
},
global: {
plugins: [Quasar, mockI18n],
@@ -1873,7 +1873,7 @@ describe('FilterGroup.vue Comprehensive Coverage', () => {
},
});
expect(wrapper.props('conditionInputWidth')).toBe('tw-w-[100px]');
expect(wrapper.props('conditionInputWidth')).toBe('tw:w-[100px]');
});
it('should have empty string as default for conditionInputWidth', () => {
@@ -2239,7 +2239,7 @@ describe('FilterGroup.vue Comprehensive Coverage', () => {
});
// Should find preview section at depth 0
const previewSection = wrapper.find('.tw-mb-2.tw-p-2.tw-rounded.tw-border');
const previewSection = wrapper.find('.tw:mb-2.tw:p-2.tw:rounded.tw:border');
expect(previewSection.exists()).toBe(true);
expect(previewSection.text()).toContain('Preview');
});
@@ -2271,7 +2271,7 @@ describe('FilterGroup.vue Comprehensive Coverage', () => {
});
// Should not find preview section at depth > 0
const previewSection = wrapper.find('.tw-mb-2.tw-p-2.tw-rounded.tw-border');
const previewSection = wrapper.find('.tw:mb-2.tw:p-2.tw:rounded.tw:border');
expect(previewSection.exists()).toBe(false);
});
@@ -2304,7 +2304,7 @@ describe('FilterGroup.vue Comprehensive Coverage', () => {
expect(wrapper.vm.showPreview).toBe(true);
// Find and click the preview header
const previewHeader = wrapper.find('.tw-cursor-pointer');
const previewHeader = wrapper.find('.tw:cursor-pointer');
await previewHeader.trigger('click');
expect(wrapper.vm.showPreview).toBe(false);
+20 -20
View File
@@ -1,29 +1,29 @@
<template>
<!-- Preview Section (only for root level) -->
<div v-if="depth === 0 && previewString"
class="tw-mb-2 tw-p-2 tw-rounded tw-border tw-w-full"
:class="store.state.theme === 'dark' ? 'tw-bg-gray-800 tw-border-gray-700' : 'tw-bg-gray-50 tw-border-gray-300'">
<div class="tw-flex tw-items-center tw-gap-1 tw-cursor-pointer tw-min-w-0" @click="showPreview = !showPreview">
class="tw:mb-2 tw:p-2 tw:rounded tw:border tw:w-full"
:class="store.state.theme === 'dark' ? 'tw:bg-gray-800 tw:border-gray-700' : 'tw:bg-gray-50 tw:border-gray-300'">
<div class="tw:flex tw:items-center tw:gap-1 tw:cursor-pointer tw:min-w-0" @click="showPreview = !showPreview">
<q-icon
:name="showPreview ? 'expand_more' : 'chevron_right'"
size="16px"
class="tw-flex-shrink-0"
:class="store.state.theme === 'dark' ? 'tw-text-gray-400' : 'tw-text-gray-600'"
class="tw:flex-shrink-0"
:class="store.state.theme === 'dark' ? 'tw:text-gray-400' : 'tw:text-gray-600'"
/>
<span class="tw-font-medium tw-text-xs tw-flex-shrink-0"
:class="store.state.theme === 'dark' ? 'tw-text-gray-300' : 'tw-text-gray-700'">
<span class="tw:font-medium tw:text-xs tw:flex-shrink-0"
:class="store.state.theme === 'dark' ? 'tw:text-gray-300' : 'tw:text-gray-700'">
Preview:
</span>
<span v-if="showPreview"
class="tw-text-[10px] tw-font-mono tw-leading-[1.3] tw-min-w-0 tw-break-words"
:class="store.state.theme === 'dark' ? 'tw-text-gray-400' : 'tw-text-gray-600'">
class="tw:text-[10px] tw:font-mono tw:leading-[1.3] tw:min-w-0 tw:break-words"
:class="store.state.theme === 'dark' ? 'tw:text-gray-400' : 'tw:text-gray-600'">
{{ previewString }}
</span>
</div>
</div>
<div :class="[` tw-px-2 tw-mb-2 el-border tw-mt-6 el-border-radius `,
store.state.isAiChatEnabled ? `tw-w-full tw-ml-[${depth * 10}px]` : `xl:tw-w-fit tw-ml-[${depth * 20}px]`
<div :class="[` tw:px-2 tw:mb-2 el-border tw:mt-6 el-border-radius `,
store.state.isAiChatEnabled ? `tw:w-full tw:ml-[${depth * 10}px]` : `xl:tw:w-fit tw:ml-[${depth * 20}px]`
]"
:style="{
opacity: computedOpacity,
@@ -32,23 +32,23 @@
>
<!-- V2: Group-level toggle only for nested groups (depth > 0) -->
<!-- Root group (depth 0) doesn't need toggle - its logicalOperator is dummy -->
<div v-if="depth > 0" class="tw-w-fit condition-tabs el-border">
<div v-if="depth > 0" class="tw:w-fit condition-tabs el-border">
<AppTabs
data-test="scheduled-alert-tabs"
:tabs="tabOptions"
class="tw-h-[20px] custom-tabs-selection-container"
class="tw:h-[20px] custom-tabs-selection-container"
v-model:active-tab="label"
@update:active-tab="toggleLabel"
/>
</div>
<!-- Spacer for root group to maintain consistent spacing -->
<div v-else class="tw-h-[14px]"></div>
<div v-else class="tw:h-[14px]"></div>
<!-- Group content -->
<div v-if="isOpen" class="tw-overflow-x-auto group-container" :class="store.state.theme === 'dark' ? 'dark-mode-group' : 'light-mode-group'">
<div v-if="isOpen" class="tw:overflow-x-auto group-container" :class="store.state.theme === 'dark' ? 'dark-mode-group' : 'light-mode-group'">
<!-- Items in group (V2 uses 'conditions' array) -->
<div class="tw-ml-2 tw-whitespace-nowrap " v-for="(item, index) in props.group.conditions" :key="index">
<div class="tw:ml-2 tw:whitespace-nowrap " v-for="(item, index) in props.group.conditions" :key="index">
<FilterGroup
v-if="isGroup(item)"
:group="item"
@@ -65,8 +65,8 @@
/>
<div
v-else
class="tw-flex tw-items-center tw-gap-2 "
:class="store.state.isAiChatEnabled ? 'tw-pl-0' : 'tw-pl-4'"
class="tw:flex tw:items-center tw:gap-2 "
:class="store.state.isAiChatEnabled ? 'tw:pl-0' : 'tw:pl-4'"
>
<FilterCondition
:condition="item"
@@ -80,14 +80,14 @@
:allow-custom-columns="props.allowCustomColumns"
:module="props.module"
/>
<div class="tw-mb-3">
<div class="tw:mb-3">
<q-btn data-test="alert-conditions-delete-condition-btn" icon="close" size="10px" flat border-less @click="removeCondition(item.id)" />
</div>
</div>
</div>
<!-- Action buttons -->
<div class="flex justify-start items-center tw-ml-4"
<div class="flex justify-start items-center tw:ml-4"
>
<q-btn
data-test="alert-conditions-add-condition-btn"
+3 -3
View File
@@ -32,7 +32,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
>
<!-- Custom URL Input Section with Folder Dropdown -->
<template #url-input-section="{ url, updateUrl }">
<div class="flex tw-mt-[0.725rem] tw-h-[64px]">
<div class="flex tw:mt-[0.725rem] tw:h-[64px]">
<div style="width: calc(69%)" class="q-pr-sm">
<q-input
data-test="alert-import-url-input"
@@ -98,11 +98,11 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<template #output-content>
<div
v-if="alertErrorsToDisplay.length > 0"
class="text-center text-h6 tw-py-2"
class="text-center text-h6 tw:py-2"
>
Error Validations
</div>
<div v-else class="text-center text-h6 tw-py-2">Output Messages</div>
<div v-else class="text-center text-h6 tw:py-2">Output Messages</div>
<q-separator class="q-mx-md q-mt-md" />
<div class="error-report-container" style="height: calc(100vh - 192px) !important; overflow: auto; resize: none;">
<!-- Alert Errors Section -->
@@ -34,14 +34,14 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
>
<!-- Output Section with Destination-specific Error Display -->
<template #output-content>
<div class="tw-w-full" style="min-width: 400px;">
<div class="tw:w-full" style="min-width: 400px;">
<div
v-if="destinationErrorsToDisplay.length > 0 || destinationCreators.length > 0"
class="text-center text-h6 tw-py-2"
class="text-center text-h6 tw:py-2"
>
{{ destinationErrorsToDisplay.length > 0 ? 'Error Validations' : 'Output Messages' }}
</div>
<div v-else class="text-center text-h6 tw-py-2">Output Messages</div>
<div v-else class="text-center text-h6 tw:py-2">Output Messages</div>
<q-separator class="q-mx-md q-mt-md" />
<div class="error-report-container">
<!-- Destination Errors Section -->
+3 -3
View File
@@ -34,14 +34,14 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
>
<!-- Output Section with Template-specific Error Display -->
<template #output-content>
<div class="tw-w-full" style="min-width: 400px;">
<div class="tw:w-full" style="min-width: 400px;">
<div
v-if="templateErrorsToDisplay.length > 0 || tempalteCreators.length > 0"
class="text-center text-h6 tw-py-2"
class="text-center text-h6 tw:py-2"
>
{{ templateErrorsToDisplay.length > 0 ? 'Error Validations' : 'Output Messages' }}
</div>
<div v-else class="text-center text-h6 tw-py-2">Output Messages</div>
<div v-else class="text-center text-h6 tw:py-2">Output Messages</div>
<q-separator class="q-mx-md q-mt-md" />
<div class="error-report-container">
<!-- Template Errors Section -->
@@ -708,23 +708,23 @@ describe("IncidentDetailDrawer.vue", () => {
const content = "This is **bold** text";
const formatted = wrapper.vm.formatRcaContent(content);
expect(formatted).toContain('<strong class="tw-font-semibold">bold</strong>');
expect(formatted).toContain('<strong class="tw:font-semibold">bold</strong>');
});
it("should format h2 headers", () => {
const content = "## Header 2";
const formatted = wrapper.vm.formatRcaContent(content);
expect(formatted).toContain("tw-font-bold");
expect(formatted).toContain("tw-text-lg");
expect(formatted).toContain("tw-text-blue-600");
expect(formatted).toContain("tw:font-bold");
expect(formatted).toContain("tw:text-lg");
expect(formatted).toContain("tw:text-blue-600");
});
it("should format h3 headers", () => {
const content = "### Header 3";
const formatted = wrapper.vm.formatRcaContent(content);
expect(formatted).toContain("tw-font-semibold");
expect(formatted).toContain("tw:font-semibold");
});
it("should format unordered lists", () => {
@@ -748,8 +748,8 @@ describe("IncidentDetailDrawer.vue", () => {
const content = "## Root Cause\n\n**Issue**: High CPU\n\n- Check process\n- Review logs";
const formatted = wrapper.vm.formatRcaContent(content);
expect(formatted).toContain('<strong class="tw-font-semibold">Issue</strong>');
expect(formatted).toContain("tw-font-bold");
expect(formatted).toContain('<strong class="tw:font-semibold">Issue</strong>');
expect(formatted).toContain("tw:font-bold");
expect(formatted).toContain("rca-ul");
});
});
+167 -167
View File
@@ -17,45 +17,45 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<template>
<div
:class="[store.state.theme === 'dark' ? 'bg-dark' : 'bg-white']"
class="tw-h-full tw-flex tw-flex-col"
class="tw:h-full tw:flex tw:flex-col"
style="width: 94vw"
>
<!-- Header -->
<div class="incident-detail-header row items-center no-wrap q-px-md tw-p-4">
<div class="incident-detail-header row items-center no-wrap q-px-md tw:p-4">
<div class="col">
<div class="tw-flex tw-items-center tw-gap-3 tw-flex-wrap">
<div class="tw-text-[18px]">
<div class="tw:flex tw:items-center tw:gap-3 tw:flex-wrap">
<div class="tw:text-[18px]">
{{ t("alerts.incidents.header") }}
</div>
<!-- Incident name with colored indicator -->
<span
:class="[
'tw-font-bold tw-px-2 tw-py-1 tw-rounded-md tw-max-w-xs tw-truncate tw-inline-block',
'tw:font-bold tw:px-2 tw:py-1 tw:rounded-md tw:max-w-xs tw:truncate tw:inline-block',
store.state.theme === 'dark'
? 'tw-text-blue-400 tw-bg-blue-900/50'
: 'tw-text-blue-600 tw-bg-blue-50'
? 'tw:text-blue-400 tw:bg-blue-900/50'
: 'tw:text-blue-600 tw:bg-blue-50'
]"
data-test="incident-detail-title"
>
{{ incidentDetails?.title }}
<q-tooltip v-if="incidentDetails && incidentDetails.title.length > 35" class="tw-text-xs">
<q-tooltip v-if="incidentDetails && incidentDetails.title.length > 35" class="tw:text-xs">
{{ incidentDetails.title }}
</q-tooltip>
</span>
<!-- Compact Status, Severity, Alerts badges -->
<div v-if="incidentDetails" class="tw-flex tw-items-center tw-gap-2">
<div v-if="incidentDetails" class="tw:flex tw:items-center tw:gap-2">
<!-- Status Badge -->
<q-badge
:color="getStatusColor(incidentDetails.status)"
class="tw-px-2.5 tw-py-1.5 tw-cursor-default"
class="tw:px-2.5 tw:py-1.5 tw:cursor-default"
outline
>
<div class="tw-flex tw-items-center tw-gap-1.5">
<div class="tw:flex tw:items-center tw:gap-1.5">
<q-icon name="info" size="14px" />
<span>{{ getStatusLabel(incidentDetails.status) }}</span>
</div>
<q-tooltip :delay="200" class="tw-text-xs">
<q-tooltip :delay="200" class="tw:text-xs">
{{ t("alerts.incidents.status") }}: {{ getStatusLabel(incidentDetails.status) }}
</q-tooltip>
</q-badge>
@@ -63,13 +63,13 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<!-- Severity Badge -->
<q-badge
:style="{ backgroundColor: getSeverityColorHex(incidentDetails.severity), color: '#fff' }"
class="tw-px-2.5 tw-py-1.5 tw-cursor-default"
class="tw:px-2.5 tw:py-1.5 tw:cursor-default"
>
<div class="tw-flex tw-items-center tw-gap-1.5">
<div class="tw:flex tw:items-center tw:gap-1.5">
<q-icon name="warning" size="14px" />
<span>{{ incidentDetails.severity }}</span>
</div>
<q-tooltip :delay="200" class="tw-text-xs">
<q-tooltip :delay="200" class="tw:text-xs">
{{ t("alerts.incidents.severity") }}: {{ incidentDetails.severity }}
</q-tooltip>
</q-badge>
@@ -77,14 +77,14 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<!-- Alert Count Badge -->
<q-badge
color="primary"
class="tw-px-2.5 tw-py-1.5 tw-cursor-default"
class="tw:px-2.5 tw:py-1.5 tw:cursor-default"
outline
>
<div class="tw-flex tw-items-center tw-gap-1.5">
<div class="tw:flex tw:items-center tw:gap-1.5">
<q-icon name="notifications_active" size="14px" />
<span>{{ incidentDetails.alert_count }} Alerts</span>
</div>
<q-tooltip :delay="200" class="tw-text-xs">
<q-tooltip :delay="200" class="tw:text-xs">
{{ t("alerts.incidents.alertCount") }}: {{ incidentDetails.alert_count }} correlated alerts
</q-tooltip>
</q-badge>
@@ -104,70 +104,70 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<q-separator />
<!-- Content -->
<div v-if="!loading && incidentDetails" class="tw-flex-1 tw-flex tw-overflow-hidden">
<div v-if="!loading && incidentDetails" class="tw:flex-1 tw:flex tw:overflow-hidden">
<!-- Left Column: Incident Details -->
<div class="incident-details-column tw-w-[400px] tw-flex-shrink-0 tw-flex tw-flex-col" :class="store.state.theme === 'dark' ? 'tw-border-r tw-border-gray-700' : 'tw-border-r tw-border-gray-200'">
<div class="incident-details-column tw:w-[400px] tw:flex-shrink-0 tw:flex tw:flex-col" :class="store.state.theme === 'dark' ? 'tw:border-r tw:border-gray-700' : 'tw:border-r tw:border-gray-200'">
<!-- Top Section (52% height) - Table of Contents -->
<div
style="height: 52%"
class="tw-border-b tw-p-4 tw-flex tw-flex-col"
:class="store.state.theme === 'dark' ? 'tw-border-gray-700' : 'tw-border-gray-200'"
class="tw:border-b tw:p-4 tw:flex tw:flex-col"
:class="store.state.theme === 'dark' ? 'tw:border-gray-700' : 'tw:border-gray-200'"
>
<div
:class="[
'tw-rounded-lg tw-border tw-overflow-hidden tw-flex tw-flex-col tw-flex-1',
'tw:rounded-lg tw:border tw:overflow-hidden tw:flex tw:flex-col tw:flex-1',
store.state.theme === 'dark'
? 'tw-border-gray-700'
: 'tw-border-gray-200'
? 'tw:border-gray-700'
: 'tw:border-gray-200'
]"
>
<!-- Header -->
<div
:class="[
'tw-px-3 tw-py-2 tw-flex tw-items-center tw-gap-2 tw-border-b tw-flex-shrink-0',
'tw:px-3 tw:py-2 tw:flex tw:items-center tw:gap-2 tw:border-b tw:flex-shrink-0',
store.state.theme === 'dark'
? 'tw-bg-gray-800 tw-border-gray-700'
: 'tw-bg-gray-100 tw-border-gray-200'
? 'tw:bg-gray-800 tw:border-gray-700'
: 'tw:bg-gray-100 tw:border-gray-200'
]"
>
<q-icon name="format_list_bulleted" size="16px" class="tw-opacity-80" />
<span :class="store.state.theme === 'dark' ? 'tw-text-gray-300' : 'tw-text-gray-700'" class="tw-text-xs tw-font-semibold">
<q-icon name="format_list_bulleted" size="16px" class="tw:opacity-80" />
<span :class="store.state.theme === 'dark' ? 'tw:text-gray-300' : 'tw:text-gray-700'" class="tw:text-xs tw:font-semibold">
Table of Contents
</span>
</div>
<!-- Content -->
<div :class="store.state.theme === 'dark' ? 'tw-bg-gray-800/30' : 'tw-bg-white'" class="tw-p-3 tw-flex-1 tw-overflow-auto">
<div v-if="tableOfContents.length === 0" :class="store.state.theme === 'dark' ? 'tw-text-gray-500' : 'tw-text-gray-400'" class="tw-text-xs tw-italic">
<div :class="store.state.theme === 'dark' ? 'tw:bg-gray-800/30' : 'tw:bg-white'" class="tw:p-3 tw:flex-1 tw:overflow-auto">
<div v-if="tableOfContents.length === 0" :class="store.state.theme === 'dark' ? 'tw:text-gray-500' : 'tw:text-gray-400'" class="tw:text-xs tw:italic">
No sections available
</div>
<div v-else class="tw-space-y-1">
<div v-else class="tw:space-y-1">
<!-- TOC Items -->
<template v-for="item in tableOfContents" :key="item.id">
<div>
<!-- Level 1 Item -->
<div
:class="[
'tw-flex tw-items-center tw-gap-2 tw-px-2 tw-py-1.5 tw-rounded tw-transition-colors',
'tw:flex tw:items-center tw:gap-2 tw:px-2 tw:py-1.5 tw:rounded tw:transition-colors',
store.state.theme === 'dark'
? 'tw-text-gray-200'
: 'tw-text-gray-900'
? 'tw:text-gray-200'
: 'tw:text-gray-900'
]"
>
<!-- Icon on the left -->
<q-icon
:name="item.children.length > 0 ? 'folder' : 'article'"
size="14px"
class="tw-opacity-60 tw-flex-shrink-0"
class="tw:opacity-60 tw:flex-shrink-0"
/>
<!-- Text in the middle - clickable to scroll -->
<span
@click="scrollToSection(item.id)"
:class="[
'tw-text-xs tw-font-medium tw-truncate tw-flex-1 tw-cursor-pointer',
'tw:text-xs tw:font-medium tw:truncate tw:flex-1 tw:cursor-pointer',
store.state.theme === 'dark'
? 'hover:tw-text-blue-400'
: 'hover:tw-text-blue-600'
? 'hover:tw:text-blue-400'
: 'hover:tw:text-blue-600'
]"
>{{ item.text }}</span>
<!-- Expand button on the right (only for items with children) -->
@@ -179,39 +179,39 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
size="xs"
:icon="expandedSections[item.id] ? 'expand_more' : 'chevron_right'"
@click="toggleSection(item, $event)"
class="tw-flex-shrink-0"
class="tw:flex-shrink-0"
>
<q-tooltip :delay="500">{{ expandedSections[item.id] ? 'Collapse' : 'Expand' }}</q-tooltip>
</q-btn>
</div>
<!-- Level 2 Children -->
<div v-if="expandedSections[item.id] && item.children.length > 0" class="tw-ml-4 tw-space-y-1 tw-mt-1">
<div v-if="expandedSections[item.id] && item.children.length > 0" class="tw:ml-4 tw:space-y-1 tw:mt-1">
<template v-for="child in item.children" :key="child.id">
<div>
<!-- Level 2 Item -->
<div
:class="[
'tw-flex tw-items-center tw-gap-2 tw-px-2 tw-py-1 tw-rounded tw-transition-colors',
'tw:flex tw:items-center tw:gap-2 tw:px-2 tw:py-1 tw:rounded tw:transition-colors',
store.state.theme === 'dark'
? 'tw-text-gray-300'
: 'tw-text-gray-700'
? 'tw:text-gray-300'
: 'tw:text-gray-700'
]"
>
<!-- Icon on the left -->
<q-icon
name="label"
size="12px"
class="tw-opacity-60 tw-flex-shrink-0"
class="tw:opacity-60 tw:flex-shrink-0"
/>
<!-- Text in the middle - clickable to scroll -->
<span
@click="scrollToSection(child.id)"
:class="[
'tw-text-xs tw-truncate tw-flex-1 tw-cursor-pointer',
'tw:text-xs tw:truncate tw:flex-1 tw:cursor-pointer',
store.state.theme === 'dark'
? 'hover:tw-text-blue-400'
: 'hover:tw-text-blue-600'
? 'hover:tw:text-blue-400'
: 'hover:tw:text-blue-600'
]"
>{{ child.text }}</span>
<!-- Expand button on the right (only for items with children) -->
@@ -223,27 +223,27 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
size="xs"
:icon="expandedSections[child.id] ? 'expand_more' : 'chevron_right'"
@click="toggleSection(child, $event)"
class="tw-flex-shrink-0"
class="tw:flex-shrink-0"
>
<q-tooltip :delay="500">{{ expandedSections[child.id] ? 'Collapse' : 'Expand' }}</q-tooltip>
</q-btn>
</div>
<!-- Level 3 Children -->
<div v-if="expandedSections[child.id] && child.children.length > 0" class="tw-ml-4 tw-space-y-1 tw-mt-1">
<div v-if="expandedSections[child.id] && child.children.length > 0" class="tw:ml-4 tw:space-y-1 tw:mt-1">
<div
v-for="grandchild in child.children"
:key="grandchild.id"
@click="scrollToSection(grandchild.id)"
:class="[
'tw-flex tw-items-center tw-gap-2 tw-px-2 tw-py-1 tw-rounded tw-cursor-pointer tw-transition-colors',
'tw:flex tw:items-center tw:gap-2 tw:px-2 tw:py-1 tw:rounded tw:cursor-pointer tw:transition-colors',
store.state.theme === 'dark'
? 'hover:tw-bg-gray-700 tw-text-gray-400'
: 'hover:tw-bg-blue-50 tw-text-gray-600'
? 'hover:tw:bg-gray-700 tw:text-gray-400'
: 'hover:tw:bg-blue-50 tw:text-gray-600'
]"
>
<q-icon name="fiber_manual_record" size="8px" class="tw-opacity-60" />
<span class="tw-text-[11px] tw-truncate">{{ grandchild.text }}</span>
<q-icon name="fiber_manual_record" size="8px" class="tw:opacity-60" />
<span class="tw:text-[11px] tw:truncate">{{ grandchild.text }}</span>
</div>
</div>
</div>
@@ -257,38 +257,38 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
</div>
<!-- Bottom Section (48% height) - Timeline, Dimensions, Topology -->
<div style="height: 48%" class="tw-overflow-auto tw-p-4">
<div style="height: 48%" class="tw:overflow-auto tw:p-4">
<!-- Timeline with UTC timestamps -->
<div
id="timeline"
:class="[
'tw-rounded-lg tw-border tw-mb-4 tw-overflow-hidden',
'tw:rounded-lg tw:border tw:mb-4 tw:overflow-hidden',
store.state.theme === 'dark'
? 'tw-border-gray-700'
: 'tw-border-gray-200'
? 'tw:border-gray-700'
: 'tw:border-gray-200'
]"
>
<!-- Header -->
<div
:class="[
'tw-px-3 tw-py-2 tw-flex tw-items-center tw-justify-between tw-border-b',
'tw:px-3 tw:py-2 tw:flex tw:items-center tw:justify-between tw:border-b',
store.state.theme === 'dark'
? 'tw-bg-gray-800 tw-border-gray-700'
: 'tw-bg-gray-100 tw-border-gray-200'
? 'tw:bg-gray-800 tw:border-gray-700'
: 'tw:bg-gray-100 tw:border-gray-200'
]"
>
<div class="tw-flex tw-items-center tw-gap-2">
<q-icon name="schedule" size="16px" class="tw-opacity-80" />
<span :class="store.state.theme === 'dark' ? 'tw-text-gray-300' : 'tw-text-gray-700'" class="tw-text-xs tw-font-semibold">
<div class="tw:flex tw:items-center tw:gap-2">
<q-icon name="schedule" size="16px" class="tw:opacity-80" />
<span :class="store.state.theme === 'dark' ? 'tw:text-gray-300' : 'tw:text-gray-700'" class="tw:text-xs tw:font-semibold">
Timeline
</span>
</div>
<span
:class="[
'tw-text-[10px] tw-font-medium tw-px-2 tw-py-0.5 tw-rounded-full',
'tw:text-[10px] tw:font-medium tw:px-2 tw:py-0.5 tw:rounded-full',
store.state.theme === 'dark'
? 'tw-text-blue-300 tw-bg-blue-900/30'
: 'tw-text-blue-700 tw-bg-blue-100'
? 'tw:text-blue-300 tw:bg-blue-900/30'
: 'tw:text-blue-700 tw:bg-blue-100'
]"
>
UTC
@@ -296,32 +296,32 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
</div>
<!-- Content -->
<div :class="store.state.theme === 'dark' ? 'tw-bg-gray-800/30' : 'tw-bg-white'" class="tw-p-3">
<div class="tw-space-y-2.5">
<div class="tw-flex tw-items-center tw-gap-2">
<q-icon name="play_arrow" size="14px" :class="store.state.theme === 'dark' ? 'tw-text-green-400' : 'tw-text-green-600'" />
<span :class="store.state.theme === 'dark' ? 'tw-text-gray-400' : 'tw-text-gray-600'" class="tw-text-[11px] tw-font-medium">
<div :class="store.state.theme === 'dark' ? 'tw:bg-gray-800/30' : 'tw:bg-white'" class="tw:p-3">
<div class="tw:space-y-2.5">
<div class="tw:flex tw:items-center tw:gap-2">
<q-icon name="play_arrow" size="14px" :class="store.state.theme === 'dark' ? 'tw:text-green-400' : 'tw:text-green-600'" />
<span :class="store.state.theme === 'dark' ? 'tw:text-gray-400' : 'tw:text-gray-600'" class="tw:text-[11px] tw:font-medium">
First Alert:
</span>
<span
:class="[
'tw-text-xs tw-font-mono',
store.state.theme === 'dark' ? 'tw-text-gray-200' : 'tw-text-gray-900'
'tw:text-xs tw:font-mono',
store.state.theme === 'dark' ? 'tw:text-gray-200' : 'tw:text-gray-900'
]"
>
{{ formatTimestampUTC(incidentDetails.first_alert_at) }}
</span>
</div>
<div class="tw-flex tw-items-center tw-gap-2">
<q-icon name="flag" size="14px" :class="store.state.theme === 'dark' ? 'tw-text-orange-400' : 'tw-text-orange-600'" />
<span :class="store.state.theme === 'dark' ? 'tw-text-gray-400' : 'tw-text-gray-600'" class="tw-text-[11px] tw-font-medium">
<div class="tw:flex tw:items-center tw:gap-2">
<q-icon name="flag" size="14px" :class="store.state.theme === 'dark' ? 'tw:text-orange-400' : 'tw:text-orange-600'" />
<span :class="store.state.theme === 'dark' ? 'tw:text-gray-400' : 'tw:text-gray-600'" class="tw:text-[11px] tw:font-medium">
Last Alert:
</span>
<span
:class="[
'tw-text-xs tw-font-mono',
store.state.theme === 'dark' ? 'tw-text-gray-200' : 'tw-text-gray-900'
'tw:text-xs tw:font-mono',
store.state.theme === 'dark' ? 'tw:text-gray-200' : 'tw:text-gray-900'
]"
>
{{ formatTimestampUTC(incidentDetails.last_alert_at) }}
@@ -332,44 +332,44 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
</div>
<!-- Dimensions & Topology - stacked -->
<div class="tw-flex tw-flex-col tw-gap-3 tw-mb-4">
<div class="tw:flex tw:flex-col tw:gap-3 tw:mb-4">
<!-- Stable Dimensions -->
<div
:class="[
'tw-rounded-lg tw-border tw-overflow-hidden',
'tw:rounded-lg tw:border tw:overflow-hidden',
store.state.theme === 'dark'
? 'tw-border-gray-700'
: 'tw-border-gray-200'
? 'tw:border-gray-700'
: 'tw:border-gray-200'
]"
>
<!-- Header -->
<div
:class="[
'tw-px-3 tw-py-2 tw-flex tw-items-center tw-gap-2 tw-border-b',
'tw:px-3 tw:py-2 tw:flex tw:items-center tw:gap-2 tw:border-b',
store.state.theme === 'dark'
? 'tw-bg-gray-800 tw-border-gray-700'
: 'tw-bg-gray-100 tw-border-gray-200'
? 'tw:bg-gray-800 tw:border-gray-700'
: 'tw:bg-gray-100 tw:border-gray-200'
]"
>
<q-icon name="category" size="16px" class="tw-opacity-80" />
<span :class="store.state.theme === 'dark' ? 'tw-text-gray-300' : 'tw-text-gray-700'" class="tw-text-xs tw-font-semibold">
<q-icon name="category" size="16px" class="tw:opacity-80" />
<span :class="store.state.theme === 'dark' ? 'tw:text-gray-300' : 'tw:text-gray-700'" class="tw:text-xs tw:font-semibold">
{{ t("alerts.incidents.stableDimensions") }}
</span>
</div>
<!-- Content -->
<div :class="store.state.theme === 'dark' ? 'tw-bg-gray-800/30' : 'tw-bg-white'" class="tw-p-3 tw-overflow-x-auto">
<div :class="store.state.theme === 'dark' ? 'tw:bg-gray-800/30' : 'tw:bg-white'" class="tw:p-3 tw:overflow-x-auto">
<div
v-for="(value, key) in incidentDetails.stable_dimensions"
:key="key"
class="tw-flex tw-text-xs tw-mb-1.5 last:tw-mb-0"
class="tw:flex tw:text-xs tw:mb-1.5 last:tw:mb-0"
>
<span :class="store.state.theme === 'dark' ? 'tw-text-gray-400' : 'tw-text-gray-600'" class="tw-mr-1 tw-font-medium tw-whitespace-nowrap">{{ key }}:</span>
<span :class="store.state.theme === 'dark' ? 'tw-text-gray-200' : 'tw-text-gray-900'" class="tw-font-mono tw-whitespace-nowrap">{{ value }}</span>
<span :class="store.state.theme === 'dark' ? 'tw:text-gray-400' : 'tw:text-gray-600'" class="tw:mr-1 tw:font-medium tw:whitespace-nowrap">{{ key }}:</span>
<span :class="store.state.theme === 'dark' ? 'tw:text-gray-200' : 'tw:text-gray-900'" class="tw:font-mono tw:whitespace-nowrap">{{ value }}</span>
</div>
<div
v-if="Object.keys(incidentDetails.stable_dimensions).length === 0"
:class="store.state.theme === 'dark' ? 'tw-text-gray-500' : 'tw-text-gray-400'"
class="tw-text-xs tw-italic"
:class="store.state.theme === 'dark' ? 'tw:text-gray-500' : 'tw:text-gray-400'"
class="tw:text-xs tw:italic"
>
No dimensions
</div>
@@ -380,54 +380,54 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<div
v-if="incidentDetails.topology_context"
:class="[
'tw-rounded-lg tw-border tw-overflow-hidden',
'tw:rounded-lg tw:border tw:overflow-hidden',
store.state.theme === 'dark'
? 'tw-border-gray-700'
: 'tw-border-gray-200'
? 'tw:border-gray-700'
: 'tw:border-gray-200'
]"
>
<!-- Header -->
<div
:class="[
'tw-px-3 tw-py-2 tw-flex tw-items-center tw-gap-2 tw-border-b',
'tw:px-3 tw:py-2 tw:flex tw:items-center tw:gap-2 tw:border-b',
store.state.theme === 'dark'
? 'tw-bg-gray-800 tw-border-gray-700'
: 'tw-bg-gray-100 tw-border-gray-200'
? 'tw:bg-gray-800 tw:border-gray-700'
: 'tw:bg-gray-100 tw:border-gray-200'
]"
>
<q-icon name="account_tree" size="16px" class="tw-opacity-80" />
<span :class="store.state.theme === 'dark' ? 'tw-text-gray-300' : 'tw-text-gray-700'" class="tw-text-xs tw-font-semibold">
<q-icon name="account_tree" size="16px" class="tw:opacity-80" />
<span :class="store.state.theme === 'dark' ? 'tw:text-gray-300' : 'tw:text-gray-700'" class="tw:text-xs tw:font-semibold">
{{ t("alerts.incidents.topology") }}
</span>
</div>
<!-- Content -->
<div :class="store.state.theme === 'dark' ? 'tw-bg-gray-800/30' : 'tw-bg-white'" class="tw-p-3">
<div class="tw-text-xs tw-mb-2">
<span :class="store.state.theme === 'dark' ? 'tw-text-gray-400' : 'tw-text-gray-600'" class="tw-font-medium">Service:</span>
<span :class="store.state.theme === 'dark' ? 'tw-text-gray-200' : 'tw-text-gray-900'" class="tw-ml-1 tw-font-mono">{{ incidentDetails.topology_context.service }}</span>
<div :class="store.state.theme === 'dark' ? 'tw:bg-gray-800/30' : 'tw:bg-white'" class="tw:p-3">
<div class="tw:text-xs tw:mb-2">
<span :class="store.state.theme === 'dark' ? 'tw:text-gray-400' : 'tw:text-gray-600'" class="tw:font-medium">Service:</span>
<span :class="store.state.theme === 'dark' ? 'tw:text-gray-200' : 'tw:text-gray-900'" class="tw:ml-1 tw:font-mono">{{ incidentDetails.topology_context.service }}</span>
</div>
<div v-if="incidentDetails.topology_context.upstream_services.length" class="tw-text-xs tw-mb-2">
<span :class="store.state.theme === 'dark' ? 'tw-text-gray-400' : 'tw-text-gray-600'" class="tw-font-medium">{{ t("alerts.incidents.upstreamServices") }}:</span>
<span class="tw-ml-1">
<div v-if="incidentDetails.topology_context.upstream_services.length" class="tw:text-xs tw:mb-2">
<span :class="store.state.theme === 'dark' ? 'tw:text-gray-400' : 'tw:text-gray-600'" class="tw:font-medium">{{ t("alerts.incidents.upstreamServices") }}:</span>
<span class="tw:ml-1">
<q-badge
v-for="svc in incidentDetails.topology_context.upstream_services"
:key="svc"
color="blue-grey-4"
:label="svc"
class="tw-mr-1 tw-mt-1"
class="tw:mr-1 tw:mt-1"
size="xs"
/>
</span>
</div>
<div v-if="incidentDetails.topology_context.downstream_services.length" class="tw-text-xs">
<span :class="store.state.theme === 'dark' ? 'tw-text-gray-400' : 'tw-text-gray-600'" class="tw-font-medium">{{ t("alerts.incidents.downstreamServices") }}:</span>
<span class="tw-ml-1">
<div v-if="incidentDetails.topology_context.downstream_services.length" class="tw:text-xs">
<span :class="store.state.theme === 'dark' ? 'tw:text-gray-400' : 'tw:text-gray-600'" class="tw:font-medium">{{ t("alerts.incidents.downstreamServices") }}:</span>
<span class="tw:ml-1">
<q-badge
v-for="svc in incidentDetails.topology_context.downstream_services"
:key="svc"
color="blue-grey-4"
:label="svc"
class="tw-mr-1 tw-mt-1"
class="tw:mr-1 tw:mt-1"
size="xs"
/>
</span>
@@ -440,9 +440,9 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
</div>
<!-- Right Column: Tabs and Content -->
<div class="tabs-content-column tw-flex-1 tw-flex tw-flex-col tw-overflow-hidden">
<div class="tabs-content-column tw:flex-1 tw:flex tw:flex-col tw:overflow-hidden">
<!-- Tabs -->
<div class="tw-flex tw-items-center tw-justify-between tw-px-4 tw-pt-4 tw-pb-2 q-px-md tw-flex-shrink-0">
<div class="tw:flex tw:items-center tw:justify-between tw:px-4 tw:pt-4 tw:pb-2 q-px-md tw:flex-shrink-0">
<q-tabs v-model="activeTab" inline-label dense no-caps align="left">
<q-tab
name="incidentAnalysis"
@@ -454,16 +454,16 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
icon="notifications_active"
>
<template #default>
<div class="tw-flex tw-items-center tw-gap-1.5">
<div class="tw:flex tw:items-center tw:gap-1.5">
<span>Alert Triggers</span>
<span class="tw-text-xs tw-opacity-70">({{ triggers.length }})</span>
<span class="tw:text-xs tw:opacity-70">({{ triggers.length }})</span>
</div>
</template>
</q-tab>
</q-tabs>
<!-- Action buttons -->
<div class="tw-flex tw-gap-2 tw-ml-auto tw-items-center">
<div class="tw:flex tw:gap-2 tw:ml-auto tw:items-center">
<q-btn
v-if="incidentDetails.status === 'open'"
color="warning"
@@ -473,9 +473,9 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
dense
@click="acknowledgeIncident"
:loading="updating"
class="tw-px-3"
class="tw:px-3"
>
<q-icon name="check_circle" size="16px" class="tw-mr-1" />
<q-icon name="check_circle" size="16px" class="tw:mr-1" />
{{ t("alerts.incidents.acknowledge") }}
<q-tooltip :delay="500">Mark incident as acknowledged and being worked on</q-tooltip>
</q-btn>
@@ -488,9 +488,9 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
dense
@click="resolveIncident"
:loading="updating"
class="tw-px-3"
class="tw:px-3"
>
<q-icon name="task_alt" size="16px" class="tw-mr-1" />
<q-icon name="task_alt" size="16px" class="tw:mr-1" />
{{ t("alerts.incidents.resolve") }}
<q-tooltip :delay="500">Mark incident as resolved and close it</q-tooltip>
</q-btn>
@@ -503,9 +503,9 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
dense
@click="reopenIncident"
:loading="updating"
class="tw-px-3"
class="tw:px-3"
>
<q-icon name="refresh" size="16px" class="tw-mr-1" />
<q-icon name="refresh" size="16px" class="tw:mr-1" />
{{ t("alerts.incidents.reopen") }}
<q-tooltip :delay="500">Reopen this resolved incident</q-tooltip>
</q-btn>
@@ -516,20 +516,20 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
flat
dense
@click="openSREChat"
class="tw-ml-2"
class="tw:ml-2"
>
<img :src="getAIIconURL()" class="tw-w-5 tw-h-5" />
<img :src="getAIIconURL()" class="tw:w-5 tw:h-5" />
<q-tooltip :delay="500" style="width: 180px;">Chat with SRE Assistant</q-tooltip>
</q-btn>
</div>
</div>
<!-- Tab Content Area -->
<div class="tw-flex-1 tw-flex tw-flex-col tw-px-4 tw-pt-2 q-px-md tw-pb-2 tw-overflow-hidden">
<div class="tw:flex-1 tw:flex tw:flex-col tw:px-4 tw:pt-2 q-px-md tw:pb-2 tw:overflow-hidden">
<!-- Incident Analysis Tab Content -->
<div v-if="activeTab === 'incidentAnalysis'" class="tw-flex tw-flex-col tw-flex-1 tw-overflow-hidden">
<div v-if="activeTab === 'incidentAnalysis'" class="tw:flex tw:flex-col tw:flex-1 tw:overflow-hidden">
<!-- Trigger button when no analysis exists and not loading -->
<div v-if="!hasExistingRca && !rcaLoading" class="tw-mb-2 tw-flex-shrink-0">
<div v-if="!hasExistingRca && !rcaLoading" class="tw:mb-2 tw:flex-shrink-0">
<q-btn
size="sm"
color="primary"
@@ -543,39 +543,39 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
</div>
<!-- Loading state with streaming content -->
<div v-if="rcaLoading" class="rca-container tw-rounded tw-p-3 tw-flex-1 tw-overflow-auto tw-border" :class="isDarkMode ? 'tw-bg-gray-800 tw-border-gray-700' : 'tw-bg-blue-50 tw-border-blue-200'">
<div class="tw-flex tw-items-center tw-gap-2 tw-mb-2">
<div v-if="rcaLoading" class="rca-container tw:rounded tw:p-3 tw:flex-1 tw:overflow-auto tw:border" :class="isDarkMode ? 'tw:bg-gray-800 tw:border-gray-700' : 'tw:bg-blue-50 tw:border-blue-200'">
<div class="tw:flex tw:items-center tw:gap-2 tw:mb-2">
<q-spinner size="sm" color="primary" />
<span class="tw-text-sm">Analysis in progress...</span>
<span class="tw:text-sm">Analysis in progress...</span>
</div>
<div
v-if="rcaStreamContent"
class="tw-text-sm tw-whitespace-pre-wrap rca-content"
class="tw:text-sm tw:whitespace-pre-wrap rca-content"
v-html="formattedRcaContent"
/>
</div>
<!-- Existing analysis content -->
<div v-else-if="hasExistingRca" class="rca-container tw-rounded tw-p-3 tw-flex-1 tw-overflow-auto tw-border" :class="isDarkMode ? 'tw-bg-gray-800 tw-border-gray-700' : 'tw-bg-blue-50 tw-border-blue-200'">
<div v-else-if="hasExistingRca" class="rca-container tw:rounded tw:p-3 tw:flex-1 tw:overflow-auto tw:border" :class="isDarkMode ? 'tw:bg-gray-800 tw:border-gray-700' : 'tw:bg-blue-50 tw:border-blue-200'">
<div
class="tw-text-sm tw-whitespace-pre-wrap rca-content"
class="tw:text-sm tw:whitespace-pre-wrap rca-content"
v-html="formattedRcaContent"
/>
</div>
<!-- No analysis yet -->
<div v-else class="tw-rounded tw-p-3 tw-text-sm tw-flex-1 tw-border" :class="isDarkMode ? 'tw-bg-gray-700 tw-border-gray-600 tw-text-gray-300' : 'tw-bg-gray-50 tw-border-gray-200 tw-text-gray-500'">
<div v-else class="tw:rounded tw:p-3 tw:text-sm tw:flex-1 tw:border" :class="isDarkMode ? 'tw:bg-gray-700 tw:border-gray-600 tw:text-gray-300' : 'tw:bg-gray-50 tw:border-gray-200 tw:text-gray-500'">
No analysis performed yet
</div>
</div>
<!-- Alert Triggers Tab Content -->
<div v-if="activeTab === 'alertTriggers'" class="tw-flex tw-flex-col tw-flex-1 tw-overflow-hidden">
<div class="tw-flex-1 tw-overflow-auto">
<q-list bordered separator class="tw-rounded">
<div v-if="activeTab === 'alertTriggers'" class="tw:flex tw:flex-col tw:flex-1 tw:overflow-hidden">
<div class="tw:flex-1 tw:overflow-auto">
<q-list bordered separator class="tw:rounded">
<q-item v-for="trigger in triggers" :key="trigger.alert_id + trigger.alert_fired_at">
<q-item-section>
<q-item-label class="tw-font-medium">
<q-item-label class="tw:font-medium">
{{ trigger.alert_name }}
</q-item-label>
<q-item-label caption>
@@ -591,7 +591,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
</q-item-section>
</q-item>
<q-item v-if="triggers.length === 0">
<q-item-section class="tw-text-gray-400">
<q-item-section class="tw:text-gray-400">
No triggers loaded
</q-item-section>
</q-item>
@@ -603,7 +603,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
</div>
<!-- Loading state -->
<div v-if="loading" class="tw-flex-1 tw-flex tw-items-center tw-justify-center">
<div v-if="loading" class="tw:flex-1 tw:flex tw:items-center tw:justify-center">
<q-spinner-hourglass size="lg" color="primary" />
</div>
</div>
@@ -1123,25 +1123,25 @@ export default defineComponent({
const id = 'section-' + rawText.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/(^-|-$)/g, '');
const classes = [
'rca-h1 tw-font-bold tw-text-lg tw-text-center tw-mb-4 tw-pb-2 tw-border-b-2',
'rca-h1 tw:font-bold tw:text-lg tw:text-center tw:mb-4 tw:pb-2 tw:border-b-2',
// TODO: Discuss with team - h2 section separators with background and left border
// Remove 'rca-section-bg tw-px-4 tw-py-3 tw-rounded tw-border-l-4 tw-border-blue-600' if not approved
'rca-h2 tw-font-bold tw-text-lg tw-mt-5 tw-mb-3 tw-text-blue-600 rca-section-bg tw-px-4 tw-py-3 tw-rounded tw-border-l-4 tw-border-blue-600',
'rca-h3 tw-font-semibold tw-text-base tw-mt-4 tw-mb-2',
'rca-h4 tw-font-semibold tw-text-sm tw-mt-3 tw-mb-2 tw-text-gray-700',
// Remove 'rca-section-bg tw:px-4 tw:py-3 tw:rounded tw:border-l-4 tw:border-blue-600' if not approved
'rca-h2 tw:font-bold tw:text-lg tw:mt-5 tw:mb-3 tw:text-blue-600 rca-section-bg tw:px-4 tw:py-3 tw:rounded tw:border-l-4 tw:border-blue-600',
'rca-h3 tw:font-semibold tw:text-base tw:mt-4 tw:mb-2',
'rca-h4 tw:font-semibold tw:text-sm tw:mt-3 tw:mb-2 tw:text-gray-700',
];
return `<h${depth} id="${id}" class="${classes[depth - 1] || ''}">${parsedText}</h${depth}>`;
return `<div id="${id}" class="${classes[depth - 1] || ''}">${parsedText}</div>`;
},
code({ text }: any) {
return `<div class="rca-code-block tw-bg-gray-100 tw-border tw-border-gray-300 tw-rounded tw-p-3 tw-my-3 tw-overflow-x-auto"><pre class="tw-text-xs tw-font-mono tw-whitespace-pre tw-m-0"><code>${text}</code></pre></div>`;
return `<div class="rca-code-block tw:bg-gray-100 tw:border tw:border-gray-300 tw:rounded tw:p-3 tw:my-3 tw:overflow-x-auto"><pre class="tw:text-xs tw:font-mono tw:whitespace-pre tw:m-0"><code>${text}</code></pre></div>`;
},
codespan({ text }: any) {
return `<code class="rca-inline-code tw-bg-gray-100 tw-px-1.5 tw-py-0.5 tw-rounded tw-text-xs tw-font-mono">${text}</code>`;
return `<code class="rca-inline-code tw:bg-gray-100 tw:px-1.5 tw:py-0.5 tw:rounded tw:text-xs tw:font-mono">${text}</code>`;
},
list(token: any) {
const body = token.items.map((item: any) => this.listitem(item)).join('');
const tag = token.ordered ? 'ol' : 'ul';
const classes = token.ordered ? 'rca-ol tw-pl-5 tw-my-3 tw-space-y-1.5 tw-list-decimal' : 'rca-ul tw-pl-5 tw-my-3 tw-space-y-1.5 tw-list-disc';
const classes = token.ordered ? 'rca-ol tw:pl-5 tw:my-3 tw:space-y-1.5 tw:list-decimal' : 'rca-ul tw:pl-5 tw:my-3 tw:space-y-1.5 tw:list-disc';
return `<${tag} class="${classes}">${body}</${tag}>`;
},
listitem(item: any) {
@@ -1154,42 +1154,42 @@ export default defineComponent({
const cell = token.header[i];
const content = this.parser.parseInline(cell.tokens);
const cellClass = i === 0 ? 'rca-first-cell' : '';
header += `<th class="tw-px-3 tw-py-2 tw-text-left tw-font-semibold tw-text-xs tw-border-b ${cellClass}">${content}</th>`;
header += `<th class="tw:px-3 tw:py-2 tw:text-left tw:font-semibold tw:text-xs tw:border-b ${cellClass}">${content}</th>`;
}
header += '</tr>';
let body = '';
for (const row of token.rows) {
body += '<tr class="hover:tw-bg-gray-50">';
body += '<tr class="hover:tw:bg-gray-50">';
for (let i = 0; i < row.length; i++) {
const cell = row[i];
const content = this.parser.parseInline(cell.tokens);
const cellClass = i === 0 ? 'rca-first-cell' : '';
body += `<td class="tw-px-3 tw-py-2 tw-text-xs tw-border-b ${cellClass}">${content}</td>`;
body += `<td class="tw:px-3 tw:py-2 tw:text-xs tw:border-b ${cellClass}">${content}</td>`;
}
body += '</tr>';
}
return `<div class="rca-table-wrapper tw-my-4 tw-overflow-x-auto"><table class="rca-table tw-w-full tw-border tw-border-gray-300 tw-rounded"><thead class="tw-bg-gray-100">${header}</thead><tbody>${body}</tbody></table></div>`;
return `<div class="rca-table-wrapper tw:my-4 tw:overflow-x-auto"><table class="rca-table tw:w-full tw:border tw:border-gray-300 tw:rounded"><thead class="tw:bg-gray-100">${header}</thead><tbody>${body}</tbody></table></div>`;
},
blockquote({ tokens }: any) {
const text = this.parser.parse(tokens);
return `<blockquote class="rca-blockquote tw-border-l-4 tw-border-blue-500 tw-pl-4 tw-py-2 tw-my-3 tw-bg-blue-50 tw-italic">${text}</blockquote>`;
return `<blockquote class="rca-blockquote tw:border-l-4 tw:border-blue-500 tw:pl-4 tw:py-2 tw:my-3 tw:bg-blue-50 tw:italic">${text}</blockquote>`;
},
paragraph({ tokens }: any) {
const text = this.parser.parseInline(tokens);
return `<p class="tw-mb-3">${text}</p>`;
return `<p class="tw:mb-3">${text}</p>`;
},
strong({ tokens }: any) {
const text = this.parser.parseInline(tokens);
return `<strong class="tw-font-semibold">${text}</strong>`;
return `<strong class="tw:font-semibold">${text}</strong>`;
},
em({ tokens }: any) {
const text = this.parser.parseInline(tokens);
return `<em class="tw-italic">${text}</em>`;
return `<em class="tw:italic">${text}</em>`;
},
hr() {
return `<hr class="tw-my-4 tw-border-t tw-border-gray-300" />`;
return `<hr class="tw:my-4 tw:border-t tw:border-gray-300" />`;
},
}
});
@@ -1418,23 +1418,23 @@ body.body--dark .muted-text {
/* Responsive scrolling */
.incident-details-column::-webkit-scrollbar,
.tabs-content-column .tw-overflow-auto::-webkit-scrollbar {
.tabs-content-column .tw:overflow-auto::-webkit-scrollbar {
width: 6px;
}
.incident-details-column::-webkit-scrollbar-track,
.tabs-content-column .tw-overflow-auto::-webkit-scrollbar-track {
.tabs-content-column .tw:overflow-auto::-webkit-scrollbar-track {
background: transparent;
}
.incident-details-column::-webkit-scrollbar-thumb,
.tabs-content-column .tw-overflow-auto::-webkit-scrollbar-thumb {
.tabs-content-column .tw:overflow-auto::-webkit-scrollbar-thumb {
background: #cbd5e1;
border-radius: 3px;
}
body.body--dark .incident-details-column::-webkit-scrollbar-thumb,
body.body--dark .tabs-content-column .tw-overflow-auto::-webkit-scrollbar-thumb {
body.body--dark .tabs-content-column .tw:overflow-auto::-webkit-scrollbar-thumb {
background: #475569;
}
</style>
+10 -10
View File
@@ -15,10 +15,10 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<template>
<div data-test="incident-list" class="tw-w-full tw-h-full tw-pl-[0.625rem] tw-pb-[0.625rem]">
<div data-test="incident-list" class="tw:w-full tw:h-full tw:pl-[0.625rem] tw:pb-[0.625rem]">
<!-- Incidents table -->
<div class="tw-w-full tw-h-full tw-pb-[0.625rem]">
<div class="card-container tw-h-[calc(100vh-127px)]">
<div class="tw:w-full tw:h-full tw:pb-[0.625rem]">
<div class="card-container tw:h-[calc(100vh-127px)]">
<q-table
ref="qTableRef"
v-model:pagination="pagination"
@@ -64,8 +64,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
/>
</template>
<template v-else-if="col.name === 'title'">
<div class="tw-flex tw-items-center tw-gap-1">
<span class="tw-font-medium">
<div class="tw:flex tw:items-center tw:gap-1">
<span class="tw:font-medium">
{{ props.row.title || formatDimensions(props.row.stable_dimensions) }}
</span>
</div>
@@ -77,7 +77,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
{{ formatTimestamp(props.row.last_alert_at) }}
</template>
<template v-else-if="col.name === 'actions'">
<div class="tw-flex tw-justify-end">
<div class="tw:flex tw:justify-end">
<q-btn
v-if="props.row.status === 'open'"
flat
@@ -138,22 +138,22 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<!-- Loading state -->
<template #loading>
<div class="tw-flex tw-items-center tw-justify-center tw-py-20">
<div class="tw:flex tw:items-center tw:justify-center tw:py-20">
<q-spinner-hourglass color="primary" size="3rem" />
</div>
</template>
<!-- Empty state -->
<template #no-data>
<div v-if="!loading" class="tw-flex tw-items-center tw-justify-center tw-w-full tw-h-full">
<div v-if="!loading" class="tw:flex tw:items-center tw:justify-center tw:w-full tw:h-full">
<no-data />
</div>
</template>
<!-- Bottom pagination -->
<template v-slot:bottom="scope">
<div class="bottom-btn tw-h-[48px]">
<div class="o2-table-footer-title tw-flex tw-items-center tw-w-[100px] tw-mr-md">
<div class="bottom-btn tw:h-[48px]">
<div class="o2-table-footer-title tw:flex tw:items-center tw:w-[100px] tw:mr-md">
{{ pagination.rowsNumber }} {{ pagination.rowsNumber === 1 ? 'Incident' : 'Incidents' }}
</div>
<QTablePagination
@@ -15,21 +15,21 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<template>
<div class="tw-w-full org-dedup-settings">
<div class="tw-mb-6">
<div class="text-h6 tw-mb-2">{{ t('alerts.correlation.title') }}</div>
<div class="tw:w-full org-dedup-settings">
<div class="tw:mb-6">
<div class="text-h6 tw:mb-2">{{ t('alerts.correlation.title') }}</div>
<div class="text-body2 text-grey-7">
{{ t('alerts.correlation.description') }}
</div>
<div class="text-body2 text-grey-6 tw-mt-2 tw-italic">
<div class="text-body2 text-grey-6 tw:mt-2 tw:italic">
{{ t('alerts.correlation.semanticFieldNote') }}
</div>
</div>
<q-separator class="tw-mb-6" />
<q-separator class="tw:mb-6" />
<!-- Enable Deduplication -->
<div class="tw-mb-6">
<div class="tw:mb-6">
<q-checkbox
v-model="localConfig.enabled"
:label="t('alerts.correlation.enableOrgLevel')"
@@ -43,7 +43,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
</div>
<!-- Cross-Alert Deduplication -->
<div class="tw-mb-6" v-if="localConfig.enabled">
<div class="tw:mb-6" v-if="localConfig.enabled">
<q-checkbox
v-model="localConfig.alert_dedup_enabled"
:label="t('alerts.correlation.enableCrossAlert')"
@@ -57,9 +57,9 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
</div>
<!-- Cross-Alert Fingerprint Groups -->
<div class="tw-mb-6" v-if="localConfig.alert_dedup_enabled">
<div class="tw-font-semibold tw-pb-2 tw-flex tw-items-center">
{{ t('alerts.correlation.fingerprintGroups') }} <span class="tw-text-red-500 tw-ml-1">*</span>
<div class="tw:mb-6" v-if="localConfig.alert_dedup_enabled">
<div class="tw:font-semibold tw:pb-2 tw:flex tw:items-center">
{{ t('alerts.correlation.fingerprintGroups') }} <span class="tw:text-red-500 tw:ml-1">*</span>
<q-icon
:name="outlinedInfo"
size="17px"
@@ -76,10 +76,10 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
</q-tooltip>
</q-icon>
</div>
<div class="tw-text-sm tw-text-gray-600 dark:tw-text-gray-400 tw-mb-2">
<div class="tw:text-sm tw:text-gray-600 dark:tw:text-gray-400 tw:mb-2">
{{ t('alerts.correlation.fingerprintGroupsHint') }}
</div>
<div class="tw-flex tw-flex-col tw-gap-2">
<div class="tw:flex tw:flex-col tw:gap-2">
<q-checkbox
v-for="group in localSemanticGroups"
:key="group.id"
@@ -90,7 +90,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
/>
<div
v-if="!localConfig.alert_fingerprint_groups || localConfig.alert_fingerprint_groups.length === 0"
class="tw-text-red-500 tw-text-sm tw-mt-1"
class="tw:text-red-500 tw:text-sm tw:mt-1"
>
{{ t('alerts.correlation.fingerprintGroupsRequired') }}
</div>
@@ -98,8 +98,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
</div>
<!-- Time Window -->
<div class="tw-mb-6">
<div class="tw-font-semibold tw-pb-2 tw-flex tw-items-center">
<div class="tw:mb-6">
<div class="tw:font-semibold tw:pb-2 tw:flex tw:items-center">
{{ t('alerts.correlation.defaultWindow') }}
<q-icon
:name="outlinedInfo"
@@ -117,7 +117,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
</q-tooltip>
</q-icon>
</div>
<div class="tw-text-sm tw-text-gray-600 dark:tw-text-gray-400 tw-mb-2">
<div class="tw:text-sm tw:text-gray-600 dark:tw:text-gray-400 tw:mb-2">
{{ t('alerts.correlation.defaultWindowDescription') }}
</div>
<q-input
@@ -136,14 +136,14 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
/>
</div>
<div class="tw-flex tw-justify-end tw-gap-3">
<q-btn outline :label="t('alerts.correlation.cancelButton')" @click="$emit('cancel')" class="tw-px-4" />
<div class="tw:flex tw:justify-end tw:gap-3">
<q-btn outline :label="t('alerts.correlation.cancelButton')" @click="$emit('cancel')" class="tw:px-4" />
<q-btn
:label="t('alerts.correlation.saveButton')"
color="primary"
@click="saveSettings"
:loading="saving"
class="tw-px-4"
class="tw:px-4"
/>
</div>
</div>
@@ -21,15 +21,15 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
>
<div v-if="!showDestinationEditor">
<div
class="tw-flex tw-justify-between tw-items-center tw-px-4 tw-py-3 tw-h-[68px] tw-border-b-[1px]"
class="tw:flex tw:justify-between tw:items-center tw:px-4 tw:py-3 tw:h-[68px] tw:border-b-[1px]"
>
<div
class="q-table__title tw-font-[600]"
class="q-table__title tw:font-[600]"
data-test="alert-destinations-list-title"
>
{{ t("pipeline_destinations.header") }}
</div>
<div class="tw-flex tw-justify-end">
<div class="tw:flex tw:justify-end">
<q-input
v-model="filterQuery"
borderless
@@ -43,7 +43,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
</q-input>
<q-btn
data-test="pipeline-destination-list-add-btn"
class="o2-primary-button q-ml-sm tw-h-[36px]"
class="o2-primary-button q-ml-sm tw:h-[36px]"
no-caps
flat
:label="t(`alert_destinations.add`)"
@@ -74,7 +74,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
</template>
<template v-slot:body-cell-actions="props">
<q-td :props="props">
<div class="tw-flex tw-justify-center tw-items-center">
<div class="tw:flex tw:justify-center tw:items-center">
<q-btn
:data-test="`alert-destination-list-${props.row.name}-update-destination`"
padding="sm"
@@ -108,14 +108,14 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
</template>
<template #bottom="scope">
<div class="tw-flex tw-items-center tw-justify-between tw-w-full tw-h-[48px]">
<div class="o2-table-footer-title tw-flex tw-items-center tw-w-[230px] tw-mr-md">
<div class="tw:flex tw:items-center tw:justify-between tw:w-full tw:h-[48px]">
<div class="o2-table-footer-title tw:flex tw:items-center tw:w-[230px] tw:mr-md">
{{ resultTotal }} {{ t('pipeline_destinations.header') }}
</div>
<q-btn
v-if="selectedDestinations.length > 0"
data-test="pipeline-destination-list-delete-destinations-btn"
class="flex items-center q-mr-sm no-border o2-secondary-button tw-h-[36px]"
class="flex items-center q-mr-sm no-border o2-secondary-button tw:h-[36px]"
:class="
store.state.theme === 'dark'
? 'o2-secondary-button-dark'
@@ -126,7 +126,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
@click="openBulkDeleteDialog"
>
<q-icon name="delete" size="16px" />
<span class="tw-ml-2">Delete</span>
<span class="tw:ml-2">Delete</span>
</q-btn>
<QTablePagination
:scope="scope"
+75 -75
View File
@@ -23,17 +23,17 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
maximized
:class="store.state.theme === 'dark' ? 'dark-mode' : 'light-mode'"
>
<div class="tw-flex tw-h-full editor-dialog-card tw-pl-1">
<div class="tw:flex tw:h-full editor-dialog-card tw:pl-1">
<div
class="tw-h-full tw-flex tw-pr-1"
class="tw:h-full tw:flex tw:pr-1"
:style="{
width: isFullScreen ? '100vw' : store.state.isAiChatEnabled ? '65vw' : '90vw'
}"
>
<div class="tw-h-full tw-w-full tw-px-2 tw-py-2">
<div class="tw:h-full tw:w-full tw:px-2 tw:py-2">
<!-- Header -->
<div class="tw-h-8 tw-flex tw-items-center tw-justify-between" style="font-size: 20px;">
<div class="tw-flex tw-items-center tw-gap-2">
<div class="tw:h-8 tw:flex tw:items-center tw:justify-between" style="font-size: 20px;">
<div class="tw:flex tw:items-center tw:gap-2">
<div
data-test="add-alert-back-btn"
class="flex justify-center items-center cursor-pointer"
@@ -43,9 +43,9 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
>
<q-icon name="arrow_back_ios_new" size="14px" />
</div>
<span class="tw-text-[18px] tw-font-[400]">Add Conditions</span>
<span class="tw:text-[18px] tw:font-[400]">Add Conditions</span>
</div>
<div class="tw-flex tw-items-center">
<div class="tw:flex tw:items-center">
<q-btn
v-if="config.isEnterprise == 'true' && store.state.zoConfig.ai_enabled"
:ripple="false"
@@ -61,7 +61,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
@mouseenter="isHovered = true"
@mouseleave="isHovered = false"
>
<div class="row items-center no-wrap tw-gap-2">
<div class="row items-center no-wrap tw:gap-2">
<img :src="getBtnLogo" class="header-icon ai-icon" />
</div>
</q-btn>
@@ -69,26 +69,26 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
icon="fullscreen"
size="16px"
dense
class="tw-cursor-pointer"
:class="store.state.theme === 'dark' ? 'tw-text-white' : ''"
class="tw:cursor-pointer"
:class="store.state.theme === 'dark' ? 'tw:text-white' : ''"
:color="isFullScreen ? 'primary' : undefined"
@click="() => isFullScreen = !isFullScreen"
/>
</div>
</div>
<q-separator class="tw-my-2"/>
<q-separator class="tw:my-2"/>
<!-- Main Content Grid -->
<div class="tw-grid tw-h-[calc(100vh-100px)] tw-w-full tw-grid-cols-[60%_40%] tw-gap-x-2">
<div class="tw:grid tw:h-[calc(100vh-100px)] tw:w-full tw:grid-cols-[60%_40%] tw:gap-x-2">
<!-- Left Section (60%) -->
<div class="tw-flex tw-w-full">
<div class="tw-flex tw-w-full tw-flex-col tw-h-full tw-gap-y-2">
<div class="tw:flex tw:w-full">
<div class="tw:flex tw:w-full tw:flex-col tw:h-full tw:gap-y-2">
<!-- SQL/PromQL Editor Section (60% of left) -->
<div class="tw-flex-[3] tw-w-full">
<div class="tw-w-full tw-h-full" :class="store.state.theme === 'dark' ? 'dark-mode' : 'light-mode'">
<div class="tw-flex tw-items-center tw-justify-between tw-pb-2 tw-pt-1">
<div class="tw:flex-[3] tw:w-full">
<div class="tw:w-full tw:h-full" :class="store.state.theme === 'dark' ? 'dark-mode' : 'light-mode'">
<div class="tw:flex tw:items-center tw:justify-between tw:pb-2 tw:pt-1">
<span class="editor-text-title">{{ localTab === 'sql' ? 'SQL Editor' : 'PromQL Editor' }}</span>
<div class="tw-flex tw-gap-2 tw-items-center tw-h-6">
<div class="tw:flex tw:gap-2 tw:items-center tw:h-6">
<div style="border: 1px solid #7980cc; border-radius: 4px; height: 32px;">
<q-btn
data-test="alert-generate-query-btn"
@@ -100,12 +100,12 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
@click="toggleAIChat"
>
<img :style="{ width: '16px', height: '16px' }" :src="getBtnO2Logo" />
<span class="tw-font-[400] tw-pl-[4px] tw-text-[12px] tw-pr-[6px] tw-py-[4px] tw-text-[#7980cc]">
<span class="tw:font-[400] tw:pl-[4px] tw:text-[12px] tw:pr-[6px] tw:py-[4px] tw:text-[#7980cc]">
{{ localTab == 'sql' ? 'Generate SQL' : 'Generate PromQL' }}
</span>
</q-btn>
</div>
<div class="tw-h-full tw-flex tw-justify-center tw-items-center o2-select-input tw-w-full col" style="padding-top: 0">
<div class="tw:h-full tw:flex tw:justify-center tw:items-center o2-select-input tw:w-full col" style="padding-top: 0">
<q-select
v-model="selectedColumn"
:options="filteredFields"
@@ -142,7 +142,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
:disable="localTab == 'sql' ? localSqlQuery == '' : localPromqlQuery == ''"
>
<q-icon name="search" size="20px" />
<span class="tw-text-[12px] tw-font-[400]">Run Query</span>
<span class="tw:text-[12px] tw:font-[400]">Run Query</span>
</q-btn>
</div>
</div>
@@ -152,14 +152,14 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
label="Input"
:isExpanded="true"
:showExpandIcon="false"
:label-class="'tw-ml-2'"
class="tw-mt-1"
:label-class="'tw:ml-2'"
class="tw:mt-1"
>
<template #right>
<div v-if="streamName" class="tw-text-[12px] tw-font-semibold tw-mr-2">
on <span class="tw-text-[14px] tw-font-bold">{{ streamName }}</span> stream
<div v-if="streamName" class="tw:text-[12px] tw:font-semibold tw:mr-2">
on <span class="tw:text-[14px] tw:font-bold">{{ streamName }}</span> stream
</div>
<div v-else class="tw-text-[12px] tw-font-semibold tw-mr-2">
<div v-else class="tw:text-[12px] tw:font-semibold tw:mr-2">
No Stream Selected
</div>
</template>
@@ -171,7 +171,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
data-test="scheduled-alert-sql-editor"
ref="queryEditorRef"
editor-id="alerts-query-editor"
class="tw-w-full"
class="tw:w-full"
:debounceTime="300"
v-model:query="localSqlQuery"
:class="[
@@ -201,7 +201,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
data-test="scheduled-alert-promql-editor"
ref="queryEditorRef"
editor-id="alerts-query-editor-dialog"
class="tw-w-full"
class="tw:w-full"
:debounceTime="300"
v-model:query="localPromqlQuery"
@update:query="updatePromqlQuery"
@@ -217,11 +217,11 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
</div>
<!-- VRL Editor Section (40% of left) -->
<div v-if="localTab !== 'promql'" class="tw-flex-[2] tw-w-full">
<div class="tw-w-full tw-h-full" :class="store.state.theme === 'dark' ? 'dark-mode' : 'light-mode'">
<div class="tw-flex tw-items-center tw-justify-between tw-pb-1 tw-pt-1">
<div v-if="localTab !== 'promql'" class="tw:flex-[2] tw:w-full">
<div class="tw:w-full tw:h-full" :class="store.state.theme === 'dark' ? 'dark-mode' : 'light-mode'">
<div class="tw:flex tw:items-center tw:justify-between tw:pb-1 tw:pt-1">
<span class="editor-text-title">VRL Editor</span>
<div class="tw-flex tw-gap-2 tw-items-center">
<div class="tw:flex tw:gap-2 tw:items-center">
<div style="border: 1px solid #7980cc; border-radius: 4px; height: 32px;">
<q-btn
data-test="alert-generate-vrl-btn"
@@ -233,12 +233,12 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
@click="toggleAIChat"
>
<img :style="{ width: '16px', height: '16px' }" :src="getBtnO2Logo" />
<span class="tw-font-[400] tw-pl-[4px] tw-text-[12px] tw-pr-[6px] tw-py-[4px] tw-text-[#7980cc]">
<span class="tw:font-[400] tw:pl-[4px] tw:text-[12px] tw:pr-[6px] tw:py-[4px] tw:text-[#7980cc]">
Generate VRL
</span>
</q-btn>
</div>
<div class="tw-h-full tw-flex tw-justify-center tw-items-center o2-select-input tw-w-full col" style="padding-top: 0;">
<div class="tw:h-full tw:flex tw:justify-center tw:items-center o2-select-input tw:w-full col" style="padding-top: 0;">
<q-select
v-model="selectedFunction"
:options="functionOptions"
@@ -280,7 +280,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
style="height: 32px;"
>
<q-icon name="search" size="18px" />
<span class="tw-text-[12px] tw-font-[400]">Apply VRL</span>
<span class="tw:text-[12px] tw:font-[400]">Apply VRL</span>
</q-btn>
</div>
</div>
@@ -290,14 +290,14 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
label="Input"
:isExpanded="true"
:showExpandIcon="false"
:label-class="'tw-ml-2'"
class="tw-mt-1"
:label-class="'tw:ml-2'"
class="tw:mt-1"
></FullViewContainer>
<QueryEditor
data-test="scheduled-alert-vrl-function-editor"
ref="fnEditorRef"
editor-id="fnEditor-dialog"
class="tw-w-full tw-h-[calc(100%-80px)]"
class="tw:w-full tw:h-[calc(100%-80px)]"
:debounceTime="300"
v-model:query="vrlFunctionContent"
:class="[
@@ -317,69 +317,69 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<!-- Right Section (40%) - Output -->
<div
class="tw-flex tw-flex-col tw-h-full tw-p-2 tw-gap-y-2 tw-overflow-y-hidden tw-transition-none"
:class="store.state.theme === 'dark' ? 'tw-bg-[#374151]' : 'tw-bg-[#F4F4F5]'"
class="tw:flex tw:flex-col tw:h-full tw:p-2 tw:gap-y-2 tw:overflow-y-hidden tw:transition-none"
:class="store.state.theme === 'dark' ? 'tw:bg-[#374151]' : 'tw:bg-[#F4F4F5]'"
>
<!-- SQL/PromQL Output -->
<div
class="tw-w-full tw-transition-none"
class="tw:w-full tw:transition-none"
:class="expandCombinedOutput ?
expandSqlOutput && localTab == 'sql' ? 'tw-flex-1 tw-h-[calc(50%-24px)]' : localTab != 'sql' ? 'tw-flex-1 tw-h-[calc(100%-24px)]' : 'tw-h-[24px]' :
expandSqlOutput ? 'tw-flex-1 tw-h-[calc(100%-24px)]' : 'tw-h-[24px]'"
expandSqlOutput && localTab == 'sql' ? 'tw:flex-1 tw:h-[calc(50%-24px)]' : localTab != 'sql' ? 'tw:flex-1 tw:h-[calc(100%-24px)]' : 'tw:h-[24px]' :
expandSqlOutput ? 'tw:flex-1 tw:h-[calc(100%-24px)]' : 'tw:h-[24px]'"
>
<div class="tw-flex tw-items-center tw-justify-between tw-w-[100%] tw-gap-2">
<div class="tw:flex tw:items-center tw:justify-between tw:w-[100%] tw:gap-2">
<FullViewContainer
name="Output"
label="Output"
class="tw-w-full"
class="tw:w-full"
:isExpanded="expandSqlOutput"
@update:isExpanded="expandSqlOutput = $event"
>
<template #right>
<div
class="tw-flex tw-items-center tw-justify-center tw-text-[12px] tw-font-semibold tw-mr-2"
:class="store.state.theme === 'dark' ? 'tw-text-white' : 'tw-text-[#6B7280]'"
class="tw:flex tw:items-center tw:justify-center tw:text-[12px] tw:font-semibold tw:mr-2"
:class="store.state.theme === 'dark' ? 'tw:text-white' : 'tw:text-[#6B7280]'"
>
Results include all multi-window additions
</div>
</template>
</FullViewContainer>
</div>
<div v-if="expandSqlOutput" class="tw-h-[calc(100%-0px)] tw-overflow-y-hidden">
<div v-if="expandSqlOutput" class="tw:h-[calc(100%-0px)] tw:overflow-y-hidden">
<!-- No output before run query -->
<div v-if="!tempRunQuery && outputEvents == ''" class="tw-flex tw-flex-col tw-justify-center tw-items-center tw-h-[calc(100%-24px)] tw-w-full no-output-before-run-query">
<div class="tw-flex tw-flex-col tw-justify-center tw-items-center tw-gap-2">
<div v-if="!tempRunQuery && outputEvents == ''" class="tw:flex tw:flex-col tw:justify-center tw:items-center tw:h-[calc(100%-24px)] tw:w-full no-output-before-run-query">
<div class="tw:flex tw:flex-col tw:justify-center tw:items-center tw:gap-2">
<q-icon
:name="outlinedLightbulb"
size="40px"
:class="store.state.theme === 'dark' ? 'tw-text-[#FB923C]' : 'tw-text-[#FB923C]'"
:class="store.state.theme === 'dark' ? 'tw:text-[#FB923C]' : 'tw:text-[#FB923C]'"
/>
<div>
<span>Please click Run Query to see the output</span>
</div>
</div>
</div>
<div v-else-if="(outputEvents == '') && !runQueryLoading" class="tw-flex tw-flex-col tw-justify-center tw-items-center tw-h-[calc(100%-24px)] no-output-before-run-query">
<div class="tw-flex tw-flex-col tw-justify-center tw-items-center tw-gap-2">
<div v-else-if="(outputEvents == '') && !runQueryLoading" class="tw:flex tw:flex-col tw:justify-center tw:items-center tw:h-[calc(100%-24px)] no-output-before-run-query">
<div class="tw:flex tw:flex-col tw:justify-center tw:items-center tw:gap-2">
<q-icon
:name="outlinedWarning"
size="40px"
class="tw-text-orange-400"
class="tw:text-orange-400"
/>
<div>
<span>{{ runPromqlError ? runPromqlError : "No results found" }}</span>
</div>
</div>
</div>
<div v-else-if="runQueryLoading" class="tw-flex tw-flex-col tw-justify-center tw-items-center tw-h-[calc(100%-24px)]">
<div v-else-if="runQueryLoading" class="tw:flex tw:flex-col tw:justify-center tw:items-center tw:h-[calc(100%-24px)]">
<q-spinner-hourglass color="primary" size="40px" />
<div class="tw-text-sm tw-text-gray-500">
<div class="tw:text-sm tw:text-gray-500">
Fetching Search Results...
</div>
</div>
<QueryEditor
v-else-if="expandSqlOutput"
class="tw-w-full tw-h-[calc(100%-24px)] tw-overflow-y-auto"
class="tw:w-full tw:h-[calc(100%-24px)] tw:overflow-y-auto"
data-test="sql-output-editor"
ref="outputEventsEditorRef"
editor-id="sql-output-editor"
@@ -394,63 +394,63 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<!-- Combined Output (SQL + VRL) -->
<div
v-if="localTab !== 'promql'"
class="tw-w-full tw-transition-none"
class="tw:w-full tw:transition-none"
:class="expandSqlOutput ?
expandCombinedOutput ? 'tw-flex-1 tw-h-[calc(50%-24px)]' : 'tw-flex-1 tw-h-[calc(100%-24px)]'
: expandCombinedOutput ? 'tw-flex-1 tw-h-[24px]' : 'tw-flex-1 tw-h-[calc(100%-24px)]'"
expandCombinedOutput ? 'tw:flex-1 tw:h-[calc(50%-24px)]' : 'tw:flex-1 tw:h-[calc(100%-24px)]'
: expandCombinedOutput ? 'tw:flex-1 tw:h-[24px]' : 'tw:flex-1 tw:h-[calc(100%-24px)]'"
>
<div class="tw-flex tw-flex-col tw-items-start tw-justify-between tw-h-fit">
<div class="tw:flex tw:flex-col tw:items-start tw:justify-between tw:h-fit">
<FullViewContainer
name="Combined Output"
label="Combined Output"
:isExpanded="expandCombinedOutput"
class="tw-w-full"
class="tw:w-full"
@update:isExpanded="expandCombinedOutput = $event"
>
<template #right>
<div
class="tw-flex tw-items-center tw-justify-center tw-text-[12px] tw-font-semibold tw-mr-2"
:class="store.state.theme === 'dark' ? 'tw-text-white' : 'tw-text-[#6B7280]'"
class="tw:flex tw:items-center tw:justify-center tw:text-[12px] tw:font-semibold tw:mr-2"
:class="store.state.theme === 'dark' ? 'tw:text-white' : 'tw:text-[#6B7280]'"
>
SQL + VRL
</div>
</template>
</FullViewContainer>
</div>
<div v-if="expandCombinedOutput && localTab !== 'promql'" class="tw-h-full">
<div v-if="!tempTestFunction && !runFnQueryLoading" class="tw-flex tw-flex-col tw-justify-center tw-items-center tw-h-[calc(100%-24px)] tw-w-full no-output-before-run-query">
<div class="tw-flex tw-flex-col tw-justify-center tw-items-center tw-gap-2">
<div v-if="expandCombinedOutput && localTab !== 'promql'" class="tw:h-full">
<div v-if="!tempTestFunction && !runFnQueryLoading" class="tw:flex tw:flex-col tw:justify-center tw:items-center tw:h-[calc(100%-24px)] tw:w-full no-output-before-run-query">
<div class="tw:flex tw:flex-col tw:justify-center tw:items-center tw:gap-2">
<q-icon
:name="outlinedLightbulb"
size="40px"
:class="store.state.theme === 'dark' ? 'tw-text-[#FB923C]' : 'tw-text-[#FB923C]'"
:class="store.state.theme === 'dark' ? 'tw:text-[#FB923C]' : 'tw:text-[#FB923C]'"
/>
<div>
<span>Please click Apply VRL to see the combined output</span>
</div>
</div>
</div>
<div v-else-if="(outputFnEvents == '') && !runFnQueryLoading && tempTestFunction" class="tw-flex tw-flex-col tw-justify-center tw-items-center tw-h-[calc(100%-24px)] no-output-before-run-query">
<div class="tw-flex tw-flex-col tw-justify-center tw-items-center tw-gap-2">
<div v-else-if="(outputFnEvents == '') && !runFnQueryLoading && tempTestFunction" class="tw:flex tw:flex-col tw:justify-center tw:items-center tw:h-[calc(100%-24px)] no-output-before-run-query">
<div class="tw:flex tw:flex-col tw:justify-center tw:items-center tw:gap-2">
<q-icon
:name="outlinedWarning"
size="40px"
class="tw-text-orange-400"
class="tw:text-orange-400"
/>
<div>
<span>No results found</span>
</div>
</div>
</div>
<div v-else-if="runFnQueryLoading" class="tw-flex tw-flex-col tw-justify-center tw-items-center tw-h-[calc(100%-24px)]">
<div v-else-if="runFnQueryLoading" class="tw:flex tw:flex-col tw:justify-center tw:items-center tw:h-[calc(100%-24px)]">
<q-spinner-hourglass color="primary" size="40px" />
<div class="tw-text-sm tw-text-gray-500">
<div class="tw:text-sm tw:text-gray-500">
Fetching Search Results...
</div>
</div>
<QueryEditor
v-else
class="tw-w-full tw-h-[calc(100%-24px)]"
class="tw:w-full tw:h-[calc(100%-24px)]"
data-test="vrl-function-test-events-output-editor"
ref="outputFnEventsEditorRef"
editor-id="test-function-events-output-editor"
@@ -976,7 +976,7 @@ const getBtnLogo = computed(() => {
}
// Force no transitions on collapsible output sections
.tw-transition-none {
.tw:transition-none {
transition: none !important;
}
</style>
+9 -9
View File
@@ -17,12 +17,12 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<template>
<q-page class="q-pa-none" style="height: calc(100vh - 88px); min-height: inherit">
<div v-if="!showImportTemplate && !showTemplateEditor" >
<div class="tw-flex tw-justify-between tw-items-center tw-px-4 tw-py-3 tw-h-[68px] tw-border-b-[1px]"
<div class="tw:flex tw:justify-between tw:items-center tw:px-4 tw:py-3 tw:h-[68px] tw:border-b-[1px]"
>
<div class="q-table__title tw-font-[600]" data-test="alert-templates-list-title">
<div class="q-table__title tw:font-[600]" data-test="alert-templates-list-title">
{{ t("alert_templates.header") }}
</div>
<div class="tw-flex tw-justify-end">
<div class="tw:flex tw:justify-end">
<q-input
v-model="filterQuery"
borderless
@@ -35,7 +35,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
</template>
</q-input>
<q-btn
class="o2-secondary-button q-ml-sm tw-h-[36px]"
class="o2-secondary-button q-ml-sm tw:h-[36px]"
no-caps
flat
:label="t(`dashboard.import`)"
@@ -44,7 +44,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
/>
<q-btn
data-test="template-list-add-btn"
class="o2-primary-button q-ml-sm tw-h-[36px]"
class="o2-primary-button q-ml-sm tw:h-[36px]"
no-caps
flat
:label="t(`alert_templates.add`)"
@@ -142,14 +142,14 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
</q-td>
</template>
<template #bottom="scope">
<div class="tw-flex tw-items-center tw-justify-between tw-w-full tw-h-[48px]">
<div class="o2-table-footer-title tw-flex tw-items-center tw-w-[150px] tw-mr-md">
<div class="tw:flex tw:items-center tw:justify-between tw:w-full tw:h-[48px]">
<div class="o2-table-footer-title tw:flex tw:items-center tw:w-[150px] tw:mr-md">
{{ resultTotal }} {{ t('alert_templates.header') }}
</div>
<q-btn
v-if="selectedTemplates.length > 0"
data-test="template-list-delete-templates-btn"
class="flex items-center q-mr-sm no-border o2-secondary-button tw-h-[36px]"
class="flex items-center q-mr-sm no-border o2-secondary-button tw:h-[36px]"
:class="
store.state.theme === 'dark'
? 'o2-secondary-button-dark'
@@ -160,7 +160,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
@click="openBulkDeleteDialog"
>
<q-icon name="delete" size="16px" />
<span class="tw-ml-2">Delete</span>
<span class="tw:ml-2">Delete</span>
</q-btn>
<QTablePagination
:scope="scope"
+3 -3
View File
@@ -17,7 +17,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<template>
<div class="col-12 q-py-sm variables-input "
:class="{
'flex tw-gap-2 items-center tw-w-full': variables.length == 0,
'flex tw:gap-2 items-center tw:w-full': variables.length == 0,
}"
>
<div class="q-pb-xs custom-input-label text-bold">
@@ -39,12 +39,12 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
</q-btn>
</div>
<template v-if="!variables.length">
<div class="flex justify-between items-center tw-ml-auto">
<div class="flex justify-between items-center tw:ml-auto">
<q-btn
data-test="alert-variables-add-btn"
size="sm"
class="text-bold no-border o2-secondary-button tw-h-[36px]"
class="text-bold no-border o2-secondary-button tw:h-[36px]"
flat
no-caps
@click="addVariable"
+7 -7
View File
@@ -16,10 +16,10 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<template>
<div class="step-advanced" :class="store.state.theme === 'dark' ? 'dark-mode' : 'light-mode'">
<div class="step-content card-container tw-px-3 tw-py-4">
<div class="step-content card-container tw:px-3 tw:py-4">
<!-- Context Variables -->
<div class="tw-mb-4">
<div class="tw-pb-2 custom-input-label text-bold">
<div class="tw:mb-4">
<div class="tw:pb-2 custom-input-label text-bold">
<span>{{ t("alerts.additionalVariables") }}</span>
<q-btn
style="color: #A0A0A0;"
@@ -41,7 +41,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<q-btn
data-test="alert-variables-add-btn"
size="sm"
class="no-border o2-secondary-button tw-h-[36px]"
class="no-border o2-secondary-button tw:h-[36px]"
flat
no-caps
@click="addVariable"
@@ -54,7 +54,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<div
v-for="(variable, index) in localVariables"
:key="variable.id"
class="tw-flex tw-items-center tw-mb-2"
class="tw:flex tw:items-center tw:mb-2"
:data-test="`alert-variables-${index + 1}`"
>
<q-input
@@ -108,7 +108,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
</div>
<!-- Description -->
<div class="tw-mb-4">
<div class="tw:mb-4">
<div class="flex items-center q-mb-sm">
<span class="text-bold custom-input-label">{{ t("alerts.description") }}</span>
</div>
@@ -131,7 +131,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
</div>
<!-- Row Template -->
<div class="tw-mb-4">
<div class="tw:mb-4">
<div class="flex items-center justify-between q-mb-sm">
<div class="flex items-center">
<span class="text-bold custom-input-label">{{ t("alerts.row") }}</span>
@@ -16,13 +16,13 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<template>
<div class="step-alert-conditions" :class="store.state.theme === 'dark' ? 'dark-mode' : 'light-mode'">
<div class="step-content card-container tw-px-3 tw-py-4">
<div class="step-content card-container tw:px-3 tw:py-4">
<q-form ref="alertSettingsForm" @submit.prevent>
<!-- For Real-Time Alerts -->
<template v-if="isRealTime === 'true'">
<!-- Silence Notification (Cooldown) -->
<div class="flex justify-start items-start tw-pb-3 tw-mb-4">
<div class="tw-font-semibold flex items-center" style="width: 190px; height: 36px">
<div class="flex justify-start items-start tw:pb-3 tw:mb-4">
<div class="tw:font-semibold flex items-center" style="width: 190px; height: 36px">
{{ t("alerts.silenceNotification") + " *" }}
<q-icon
name="info"
@@ -84,12 +84,12 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
</div>
<!-- Destinations -->
<div class="flex items-start tw-pb-4 tw-mb-4">
<div style="width: 190px; height: 36px" class="flex items-center tw-font-semibold">
<div class="flex items-start tw:pb-4 tw:mb-4">
<div style="width: 190px; height: 36px" class="flex items-center tw:font-semibold">
<span>{{ t("alerts.destination") }} *</span>
</div>
<div class="tw-flex tw-flex-col">
<div class="tw-flex tw-items-center">
<div class="tw:flex tw:flex-col">
<div class="tw:flex tw:items-center">
<q-select
v-model="localDestinations"
:options="filteredDestinations"
@@ -173,7 +173,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<!-- For Scheduled Alerts -->
<template v-else>
<!-- Aggregation Toggle (only for custom queries, not SQL or PromQL) -->
<div v-if="queryType === 'custom'" class="flex justify-start items-center tw-font-semibold alert-settings-row">
<div v-if="queryType === 'custom'" class="flex justify-start items-center tw:font-semibold alert-settings-row">
<div class="flex items-center" style="width: 190px; height: 36px">
{{ t("common.aggregation") }}
<q-icon
@@ -194,7 +194,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
v-model="localIsAggregationEnabled"
size="md"
color="primary"
class="text-bold q-pl-0 o2-toggle-button-sm tw-h-[36px] tw-ml-1"
class="text-bold q-pl-0 o2-toggle-button-sm tw:h-[36px] tw:ml-1"
@update:model-value="toggleAggregation"
/>
</div>
@@ -204,7 +204,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
v-if="localIsAggregationEnabled && formData.query_condition.aggregation"
class="flex items-start no-wrap q-mr-sm alert-settings-row"
>
<div class="flex items-center tw-font-semibold" style="width: 190px; height: 36px">
<div class="flex items-center tw:font-semibold" style="width: 190px; height: 36px">
{{ t("alerts.groupBy") }}
<q-icon
name="info"
@@ -279,7 +279,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<!-- Threshold -->
<div class="flex justify-start items-start q-mb-xs no-wrap alert-settings-row">
<div class="tw-font-semibold flex items-center" style="width: 190px; height: 36px">
<div class="tw:font-semibold flex items-center" style="width: 190px; height: 36px">
{{ t("alerts.threshold") + " *" }}
<q-icon
name="info"
@@ -298,8 +298,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<div style="width: calc(100% - 190px)">
<!-- With Aggregation -->
<template v-if="localIsAggregationEnabled && formData.query_condition.aggregation">
<div ref="thresholdFieldRef" class="flex tw-flex-col justify-start items-start tw-gap-2">
<div class="tw-flex tw-items-center">
<div ref="thresholdFieldRef" class="flex tw:flex-col justify-start items-start tw:gap-2">
<div class="tw:flex tw:items-center">
<div class="q-mr-xs">
<q-select
v-model="formData.query_condition.aggregation.function"
@@ -336,7 +336,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
</div>
</div>
<div class="flex items-center q-mt-xs">
<div class="monaco-editor-test q-mr-xs tw-pb-1">
<div class="monaco-editor-test q-mr-xs tw:pb-1">
<q-select
v-model="formData.query_condition.aggregation.having.operator"
:options="triggerOperators"
@@ -351,7 +351,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
@update:model-value="emitAggregationUpdate"
/>
</div>
<div class="flex items-center tw-pb-1">
<div class="flex items-center tw:pb-1">
<div style="width: 250px; margin-left: 0 !important">
<q-input
v-model="formData.query_condition.aggregation.having.value"
@@ -373,7 +373,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<!-- Without Aggregation -->
<template v-else>
<div ref="thresholdFieldRef" class="flex justify-start items-start">
<div class="tw-flex tw-flex-col">
<div class="tw:flex tw:flex-col">
<q-select
v-model="formData.trigger_condition.operator"
:options="triggerOperators"
@@ -400,8 +400,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
Field is required!
</div>
</div>
<div class="flex items-start tw-flex-col" style="border-left: none">
<div class="tw-flex tw-items-center">
<div class="flex items-start tw:flex-col" style="border-left: none">
<div class="tw:flex tw:items-center">
<div style="width: 89px; margin-left: 0 !important">
<q-input
v-model.number="formData.trigger_condition.threshold"
@@ -438,7 +438,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<!-- Period -->
<div class="flex items-start q-mr-sm alert-settings-row">
<div class="tw-font-semibold flex items-center" style="width: 190px; height: 36px">
<div class="tw:font-semibold flex items-center" style="width: 190px; height: 36px">
{{ t("alerts.period") + " *" }}
<q-icon
name="info"
@@ -489,7 +489,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<!-- Frequency (with inline interval/cron toggle) -->
<div class="flex items-start q-mr-sm alert-settings-row">
<div class="tw-font-semibold flex items-center" style="width: 190px; height: 36px">
<div class="tw:font-semibold flex items-center" style="width: 190px; height: 36px">
{{ t("alerts.frequency") + " *" }}
<q-icon
name="info"
@@ -514,23 +514,23 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<q-icon
name="warning"
size="18px"
class="cursor-pointer tw-ml-2"
:class="store.state.theme === 'dark' ? 'tw-text-orange-500' : 'tw-text-orange-500'"
class="cursor-pointer tw:ml-2"
:class="store.state.theme === 'dark' ? 'tw:text-orange-500' : 'tw:text-orange-500'"
>
<q-tooltip
anchor="center right"
self="center left"
max-width="auto"
class="tw-text-[14px]"
class="tw:text-[14px]"
>
Warning: The displayed timezone is approximate. Verify and select the correct timezone manually.
</q-tooltip>
</q-icon>
</template>
</div>
<div class="tw-flex tw-flex-col" style="min-height: 78px">
<div class="tw:flex tw:flex-col" style="min-height: 78px">
<!-- Interval/Cron Mode Buttons -->
<div class="tw-flex frequency-toggle-group tw-mb-3">
<div class="tw:flex frequency-toggle-group tw:mb-3">
<q-btn
:label="t('alerts.interval')"
:outline="formData.trigger_condition.frequency_type === 'cron'"
@@ -538,7 +538,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
:color="formData.trigger_condition.frequency_type === 'minutes' ? 'primary' : 'grey-7'"
no-caps
size="sm"
class="tw-px-4 frequency-toggle-btn frequency-toggle-left"
class="tw:px-4 frequency-toggle-btn frequency-toggle-left"
:class="formData.trigger_condition.frequency_type === 'minutes' ? 'active' : 'inactive'"
style="min-width: 90px"
@click="handleFrequencyTypeChange('minutes')"
@@ -550,7 +550,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
:color="formData.trigger_condition.frequency_type === 'cron' ? 'primary' : 'grey-7'"
no-caps
size="sm"
class="tw-px-4 frequency-toggle-btn frequency-toggle-right"
class="tw:px-4 frequency-toggle-btn frequency-toggle-right"
:class="formData.trigger_condition.frequency_type === 'cron' ? 'active' : 'inactive'"
style="min-width: 130px"
@click="handleFrequencyTypeChange('cron')"
@@ -558,9 +558,9 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
</div>
<!-- Input Fields Container (fixed height to prevent shifting) -->
<div class="tw-flex tw-items-start" style="min-height: 36px">
<div class="tw:flex tw:items-start" style="min-height: 36px">
<!-- Interval Mode -->
<div v-if="formData.trigger_condition.frequency_type === 'minutes'" class="tw-flex tw-items-center">
<div v-if="formData.trigger_condition.frequency_type === 'minutes'" class="tw:flex tw:items-center">
<div style="width: 87px; margin-left: 0 !important">
<q-input
v-model.number="formData.trigger_condition.frequency"
@@ -584,7 +584,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
</div>
<!-- Cron Mode -->
<div v-else class="tw-flex tw-items-center tw-gap-2">
<div v-else class="tw:flex tw:items-center tw:gap-2">
<q-input
v-model="formData.trigger_condition.cron"
dense
@@ -627,7 +627,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
(formData.trigger_condition.frequency_type === 'cron' && (!formData.trigger_condition.cron || !formData.trigger_condition.timezone)) ||
cronJobError
"
class="text-red-8 tw-mt-1"
class="text-red-8 tw:mt-1"
style="font-size: 11px; line-height: 12px"
>
{{ cronJobError || "Field is required!" }}
@@ -637,7 +637,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<!-- Silence Notification (Cooldown) for Scheduled Alerts -->
<div class="flex items-start q-mr-sm alert-settings-row">
<div class="tw-font-semibold flex items-center" style="width: 190px; height: 36px">
<div class="tw:font-semibold flex items-center" style="width: 190px; height: 36px">
{{ t("alerts.silenceNotification") + " *" }}
<q-icon
name="info"
@@ -701,7 +701,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<!-- Destinations -->
<div class="flex items-start q-mr-sm alert-settings-row">
<div class="tw-font-semibold flex items-center" style="width: 190px; height: 36px">
<div class="tw:font-semibold flex items-center" style="width: 190px; height: 36px">
{{ t("alerts.destination") + " *" }}
<q-icon
name="info"
@@ -16,10 +16,10 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<template>
<div class="step-alert-setup" :class="store.state.theme === 'dark' ? 'dark-mode' : 'light-mode'">
<div class="step-content card-container tw-px-3 tw-py-4">
<div class="step-content card-container tw:px-3 tw:py-4">
<q-form ref="step1Form" @submit.prevent>
<!-- Alert Name -->
<div class="form-field tw-mb-4">
<div class="form-field tw:mb-4">
<q-input
data-test="add-alert-name-input"
v-model="formData.name"
@@ -43,7 +43,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
</div>
<!-- Folder Selection -->
<div class="form-field tw-mb-4">
<div class="form-field tw:mb-4">
<SelectFolderDropDown
:disableDropdown="beingUpdated"
:type="'alerts'"
@@ -54,7 +54,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
</div>
<!-- Stream Type and Stream Name Row -->
<div class="tw-grid tw-grid-cols-1 md:tw-grid-cols-2 tw-gap-4">
<div class="tw:grid tw:grid-cols-1 md:tw:grid-cols-2 tw:gap-4">
<!-- Stream Type -->
<div class="form-field">
<q-select
@@ -106,8 +106,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
</div>
<!-- Alert Type (Scheduled/Real-time) -->
<div class="form-field tw-mb-4 tw-mt-4">
<div class="tw-flex tw-items-center tw-gap-5">
<div class="form-field tw:mb-4 tw:mt-4">
<div class="tw:flex tw:items-center tw:gap-5">
<q-radio
data-test="add-alert-scheduled-alert-radio"
v-bind:readonly="beingUpdated"
@@ -16,23 +16,23 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<template>
<div ref="multiWindowContainerRef" class="step-compare-with-past" :class="store.state.theme === 'dark' ? 'dark-mode' : 'light-mode'">
<div class="step-content card-container tw-px-3 tw-py-4" :class="store.state.theme === 'dark' ? 'dark-mode-multi-window' : 'light-mode-multi-window'">
<div class="step-content card-container tw:px-3 tw:py-4" :class="store.state.theme === 'dark' ? 'dark-mode-multi-window' : 'light-mode-multi-window'">
<!-- Alert set for header -->
<div class="multi-window-text tw-flex tw-items-center tw-gap-2 q-py-sm q-mt-md">
<div class="multi-window-text tw:flex tw:items-center tw:gap-2 q-py-sm q-mt-md">
<span>Alert set for</span>
<div class="tw-h-px border-line tw-flex-1"></div>
<div class="tw:h-px border-line tw:flex-1"></div>
</div>
<!-- Current Window -->
<div class="tw-flex tw-flex-col lg:tw-flex-row tw-justify-between tw-items-start multi-window-container q-px-md q-py-sm">
<div class="multi-window-text tw-w-full tw-text-center lg:tw-w-auto lg:tw-text-left">
<div class="tw:flex tw:flex-col lg:tw:flex-row tw:justify-between tw:items-start multi-window-container q-px-md q-py-sm">
<div class="multi-window-text tw:w-full tw:text-center lg:tw:w-auto lg:tw:text-left">
Current window
</div>
<div class="tw-flex lg:tw-flex-col tw-items-start tw-gap-2">
<div class="multi-window-text tw-w-full tw-text-center lg:tw-w-auto lg:tw-text-left">
<div class="tw:flex lg:tw:flex-col tw:items-start tw:gap-2">
<div class="multi-window-text tw:w-full tw:text-center lg:tw:w-auto lg:tw:text-left">
Cycle
<span class="tw-cursor-pointer">
<span class="tw:cursor-pointer">
<q-icon
name="info"
size="17px"
@@ -45,12 +45,12 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
</q-icon>
</span>
</div>
<div class="tw-flex tw-justify-between tw-items-start tw-gap-4">
<div class="tw-w-full lg:tw-w-[300px] running-text">
<div class="tw:flex tw:justify-between tw:items-start tw:gap-4">
<div class="tw:w-full lg:tw:w-[300px] running-text">
Running for {{ convertMinutesToDisplayValue(period) }} in the interval of every {{ convertMinutesToDisplayValue(frequency) }}
</div>
<div>
<span class="tw-inline-block">
<span class="tw:inline-block">
<q-btn
icon="delete_outline"
class="iconHoverBtn q-ml-xs q-mr-sm"
@@ -77,27 +77,27 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
</div>
<!-- Comparing with header -->
<div v-if="localMultiTimeRange.length > 0" class="multi-window-text tw-flex tw-items-center tw-gap-2 q-py-sm q-mt-sm">
<div v-if="localMultiTimeRange.length > 0" class="multi-window-text tw:flex tw:items-center tw:gap-2 q-py-sm q-mt-sm">
<span>Comparing with</span>
<div class="tw-h-px border-line tw-flex-1"></div>
<div class="tw:h-px border-line tw:flex-1"></div>
</div>
<!-- Reference Windows List -->
<div
v-for="(picker, index) in localMultiTimeRange"
:key="picker.uuid"
class="tw-flex tw-flex-col lg:tw-flex-row tw-justify-between tw-items-start reference-window-container tw-mt-2 q-px-md q-py-sm"
class="tw:flex tw:flex-col lg:tw:flex-row tw:justify-between tw:items-start reference-window-container tw:mt-2 q-px-md q-py-sm"
>
<div class="multi-window-text tw-w-full tw-text-center lg:tw-w-auto lg:tw-text-left">
<div class="multi-window-text tw:w-full tw:text-center lg:tw:w-auto lg:tw:text-left">
Reference Window {{ index + 1 }}
</div>
<!-- Time Frame -->
<div class="tw-flex tw-flex-col tw-gap-2 tw-items-center">
<div class="tw-flex tw-items-center">
<span class="tw-mr-1"><q-icon name="schedule" size="16px" /></span>
<div class="tw:flex tw:flex-col tw:gap-2 tw:items-center">
<div class="tw:flex tw:items-center">
<span class="tw:mr-1"><q-icon name="schedule" size="16px" /></span>
Time Frame
<span class="tw-ml-2 tw-cursor-pointer">
<span class="tw:ml-2 tw:cursor-pointer">
<q-icon
name="info"
size="17px"
@@ -110,7 +110,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
</q-icon>
</span>
</div>
<div class="datetime-picker-wrapper tw-mt-2">
<div class="datetime-picker-wrapper tw:mt-2">
<CustomDateTimePicker
v-model="picker.offSet"
:picker="picker"
@@ -122,10 +122,10 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
</div>
<!-- Cycle Info -->
<div class="tw-flex lg:tw-flex-col tw-items-start tw-gap-2">
<div class="multi-window-text tw-w-full tw-text-center lg:tw-w-auto lg:tw-text-left">
<div class="tw:flex lg:tw:flex-col tw:items-start tw:gap-2">
<div class="multi-window-text tw:w-full tw:text-center lg:tw:w-auto lg:tw:text-left">
Cycle
<span class="tw-cursor-pointer">
<span class="tw:cursor-pointer">
<q-icon
name="info"
size="17px"
@@ -138,8 +138,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
</q-icon>
</span>
</div>
<div class="tw-flex tw-justify-between tw-items-start tw-gap-4">
<div class="tw-w-full lg:tw-w-[300px] reference-text">
<div class="tw:flex tw:justify-between tw:items-start tw:gap-4">
<div class="tw:w-full lg:tw:w-[300px] reference-text">
Comparing current window query result with query result from previous {{ getDisplayValue(picker.offSet) }}.
</div>
<div>
@@ -162,7 +162,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
</div>
<!-- Action Buttons Section -->
<div class="tw-w-full tw-flex tw-justify-center tw-items-center tw-gap-3 q-mt-sm">
<div class="tw:w-full tw:flex tw:justify-center tw:items-center tw:gap-3 q-mt-sm">
<q-btn
data-test="multi-time-range-alerts-add-btn"
label="Add Comparison Window"
@@ -184,7 +184,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
</q-btn>
<!-- Go to View Editor Button with Info Icon - shows when comparison windows are added -->
<div v-if="localMultiTimeRange.length > 0" class="tw-flex tw-items-center tw-gap-2">
<div v-if="localMultiTimeRange.length > 0" class="tw:flex tw:items-center tw:gap-2">
<q-btn
data-test="go-to-view-editor-btn"
label="Go to Conditions"
@@ -458,12 +458,12 @@ describe("Deduplication.vue", () => {
describe("UI Rendering", () => {
it("should render fingerprint fields label", () => {
const label = wrapper.find(".tw-font-semibold");
const label = wrapper.find(".tw\\:font-semibold");
expect(label.exists()).toBe(true);
});
it("should render time window label", () => {
const labels = wrapper.findAll(".tw-font-semibold");
const labels = wrapper.findAll(".tw\\:font-semibold");
expect(labels.length).toBeGreaterThan(1);
});
@@ -16,10 +16,10 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<template>
<div class="step-deduplication" :class="store.state.theme === 'dark' ? 'dark-mode' : 'light-mode'">
<div class="step-content card-container tw-px-3 tw-py-4">
<div class="step-content card-container tw:px-3 tw:py-4">
<!-- Fingerprint Fields -->
<div class="tw-mb-4">
<div class="tw-font-semibold tw-pb-2 tw-flex tw-items-center">
<div class="tw:mb-4">
<div class="tw:font-semibold tw:pb-2 tw:flex tw:items-center">
{{ t("alerts.deduplication.fingerprintFields") }}
<q-icon
name="info"
@@ -32,7 +32,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
</q-tooltip>
</q-icon>
</div>
<div class="tw-text-sm tw-text-gray-600 dark:tw-text-gray-400 tw-mb-2">
<div class="tw:text-sm tw:text-gray-600 dark:tw:text-gray-400 tw:mb-2">
{{ t("alerts.deduplication.fingerprintFieldsHint") }}
</div>
<q-select
@@ -54,7 +54,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
style="width: 300px;"
>
<template v-slot:hint>
<div class="tw-text-xs">
<div class="tw:text-xs">
💡 Leave empty to auto-detect based on query (SQL: GROUP BY columns, PromQL: labels, Custom: condition
fields)
</div>
@@ -63,8 +63,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
</div>
<!-- Time Window -->
<div class="tw-mb-4">
<div class="tw-font-semibold tw-pb-2 tw-flex tw-items-center">
<div class="tw:mb-4">
<div class="tw:font-semibold tw:pb-2 tw:flex tw:items-center">
{{ t("alerts.deduplication.timeWindow") }}
<q-icon
name="info"
@@ -77,10 +77,10 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
</q-tooltip>
</q-icon>
</div>
<div class="tw-text-sm tw-text-gray-600 dark:tw-text-gray-400 tw-mb-2">
<div class="tw:text-sm tw:text-gray-600 dark:tw:text-gray-400 tw:mb-2">
{{ t("alerts.deduplication.timeWindowHint") }}
</div>
<div class="tw-flex tw-items-center">
<div class="tw:flex tw:items-center">
<div style="width: 210px; margin-left: 0 !important">
<q-input
v-model.number="localDeduplication.time_window_minutes"
+11 -11
View File
@@ -16,10 +16,10 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<template>
<div class="step-query-config" :class="store.state.theme === 'dark' ? 'dark-mode' : 'light-mode'">
<div class="step-content card-container tw-px-3 tw-py-4">
<div class="step-content card-container tw:px-3 tw:py-4">
<!-- Query Mode Tabs (hidden for real-time alerts) -->
<div v-if="shouldShowTabs" class="tw-mb-4 tw-flex tw-items-center tw-justify-between">
<div class="flex items-center app-tabs-container tw-h-[36px] tw-w-fit">
<div v-if="shouldShowTabs" class="tw:mb-4 tw:flex tw:items-center tw:justify-between">
<div class="flex items-center app-tabs-container tw:h-[36px] tw:w-fit">
<AppTabs
data-test="step2-query-tabs"
:tabs="tabOptions"
@@ -66,26 +66,26 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<!-- SQL/PromQL Preview Mode -->
<template v-else>
<div class="tw-w-full tw-flex tw-flex-col tw-gap-4">
<div class="tw:w-full tw:flex tw:flex-col tw:gap-4">
<!-- Preview Boxes Container -->
<div class="tw-flex tw-gap-4 tw-w-full">
<div class="tw:flex tw:gap-4 tw:w-full">
<!-- SQL/PromQL Preview Box (50% or 100% if no VRL) -->
<div ref="sqlPromqlPreviewRef" class="preview-box tw-flex-1" :class="store.state.theme === 'dark' ? 'dark-mode-preview' : 'light-mode-preview'" style="height: 464px;">
<div class="preview-header tw-flex tw-items-center tw-justify-between tw-px-3 tw-py-2">
<div ref="sqlPromqlPreviewRef" class="preview-box tw:flex-1" :class="store.state.theme === 'dark' ? 'dark-mode-preview' : 'light-mode-preview'" style="height: 464px;">
<div class="preview-header tw:flex tw:items-center tw:justify-between tw:px-3 tw:py-2">
<span class="preview-title">{{ localTab === 'sql' ? 'SQL' : 'PromQL' }} Preview</span>
</div>
<div class="preview-content tw-px-3 tw-py-2">
<div class="preview-content tw:px-3 tw:py-2">
<pre class="preview-code">{{ sqlOrPromqlQuery || `No ${localTab === 'sql' ? 'SQL' : 'PromQL'} query defined yet` }}</pre>
</div>
</div>
<!-- VRL Preview Box (50%) - Only show if VRL function exists -->
<div v-if="vrlFunction" class="preview-box tw-flex-1" :class="store.state.theme === 'dark' ? 'dark-mode-preview' : 'light-mode-preview'" style="height: 464px;">
<div class="preview-header tw-flex tw-items-center tw-justify-between tw-px-3 tw-py-2">
<div v-if="vrlFunction" class="preview-box tw:flex-1" :class="store.state.theme === 'dark' ? 'dark-mode-preview' : 'light-mode-preview'" style="height: 464px;">
<div class="preview-header tw:flex tw:items-center tw:justify-between tw:px-3 tw:py-2">
<span class="preview-title">VRL Preview</span>
</div>
<div class="preview-content tw-px-3 tw-py-2">
<div class="preview-content tw:px-3 tw:py-2">
<pre class="preview-code">{{ vrlFunction }}</pre>
</div>
</div>
@@ -16,13 +16,13 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<template>
<q-page class="q-pa-none" style="min-height: inherit;">
<div class="row items-center no-wrap q-mx-md q-pt-sm">
<div class="flex items-center tw-py-2">
<div class="flex items-center tw:py-2">
<div
no-caps
padding="xs"
outline
icon="arrow_back_ios_new"
class="el-border tw-w-6 tw-h-6 flex items-center justify-center cursor-pointer el-border-radius q-mr-sm"
class="el-border tw:w-6 tw:h-6 flex items-center justify-center cursor-pointer el-border-radius q-mr-sm"
title="Go Back"
@click="$emit('cancel:hideform')"
>
@@ -128,7 +128,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<q-btn
data-test="add-report-step1-continue-btn"
@click="validateForm(2)"
class="o2-primary-button tw-h-[36px]"
class="o2-primary-button tw:h-[36px]"
flat
no-caps
:label="'Continue'"
@@ -149,7 +149,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
data-test="add-cipher-key-step2-back-btn"
flat
@click="step = 1"
class="o2-secondary-button tw-h-[36px] q-mb-sm"
class="o2-secondary-button tw:h-[36px] q-mb-sm"
:label="t('common.back')"
no-caps
/>
@@ -157,13 +157,13 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
</q-step>
</q-stepper>
</div>
<div class="tw-mx-2">
<div class="tw:mx-2">
<div class="flex justify-end q-px-sm q-py-lg full-width"
style="position: sticky; bottom: 0px; z-index: 2"
>
<q-btn
data-test="add-cipher-key-cancel-btn"
class="q-mr-md o2-secondary-button tw-h-[36px]"
class="q-mr-md o2-secondary-button tw:h-[36px]"
:label="t('common.cancel')"
no-caps
flat
@@ -174,7 +174,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
(step === 1 && isUpdatingCipherKey == false) || isSubmitting
"
data-test="add-cipher-key-save-btn"
class="o2-primary-button no-border tw-h-[36px]"
class="o2-primary-button no-border tw:h-[36px]"
:label="t('common.save')"
type="submit"
no-caps
+20 -20
View File
@@ -15,10 +15,10 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<template>
<div class="tw-w-full" :class="containerClass" :style="containerStyle">
<div class="tw:w-full" :class="containerClass" :style="containerStyle">
<!-- Header Section -->
<div class="card-container tw-mb-[0.625rem]" :class="headerContainerClass">
<div class="flex tw-px-4 items-center no-wrap tw-h-[68px]" :class="headerClass">
<div class="card-container tw:mb-[0.625rem]" :class="headerContainerClass">
<div class="flex tw:px-4 items-center no-wrap tw:h-[68px]" :class="headerClass">
<div class="col">
<div class="flex">
<q-btn
@@ -39,7 +39,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<div class="flex justify-center">
<q-btn
v-close-popup
class="q-mr-md o2-secondary-button tw-h-[36px]"
class="q-mr-md o2-secondary-button tw:h-[36px]"
:label="t('function.cancel')"
no-caps
flat
@@ -48,7 +48,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
:data-test="`${testPrefix}-import-cancel-btn`"
/>
<q-btn
class="o2-primary-button no-border tw-h-[36px]"
class="o2-primary-button no-border tw:h-[36px]"
:label="t('dashboard.import')"
type="submit"
no-caps
@@ -74,10 +74,10 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
:limits="[30, 60]"
>
<template #before>
<div class="tw-w-full tw-h-full">
<div class="tw:w-full tw:h-full">
<!-- Tabs Section -->
<div class="card-container tw-py-[0.625rem] tw-px-[0.625rem] tw-mb-[0.625rem]">
<div class="app-tabs-container tw-h-[36px] tw-w-fit">
<div class="card-container tw:py-[0.625rem] tw:px-[0.625rem] tw:mb-[0.625rem]">
<div class="app-tabs-container tw:h-[36px] tw:w-fit">
<app-tabs
:data-test="`${testPrefix}-import-tabs`"
class="tabs-selection-container"
@@ -91,12 +91,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 tw-py-1"
class="editor-container-url card-container tw:py-1"
>
<q-form class="tw-mx-2 tw-pb-2" @submit.prevent>
<q-form class="tw:mx-2 tw:pb-2" @submit.prevent>
<!-- Slot for custom URL input section -->
<slot name="url-input-section" :url="url" :updateUrl="updateUrl">
<div class="flex tw-mt-[0.725rem] tw-h-[64px]">
<div class="flex tw:mt-[0.725rem] tw:h-[64px]">
<div style="width: 100%" class="q-pr-sm">
<q-input
:data-test="`${testPrefix}-import-url-input`"
@@ -114,7 +114,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="monaco-editor tw-mx-2"
class="monaco-editor tw:mx-2"
:debounceTime="300"
v-model:query="jsonStr"
language="json"
@@ -132,9 +132,9 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<!-- File Upload Tab -->
<div
v-if="activeTab === 'import_json_file'"
class="editor-container-json card-container tw-py-1"
class="editor-container-json card-container tw:py-1"
>
<q-form class="tw-mx-2 q-mt-md tw-pb-2" @submit.prevent>
<q-form class="tw:mx-2 q-mt-md tw:pb-2" @submit.prevent>
<!-- Slot for custom file input section -->
<slot name="file-input-section" :jsonFiles="jsonFiles" :updateFiles="updateFiles">
<div style="width: calc(100% - 10px)" class="q-mb-xs flex">
@@ -169,7 +169,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="monaco-editor tw-mx-2"
class="monaco-editor tw:mx-2"
:debounceTime="300"
v-model:query="jsonStr"
language="json"
@@ -192,14 +192,14 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<template #after>
<div
:data-test="`${testPrefix}-import-output-editor`"
class="card-container tw-mb-[0.625rem] tw-w-full"
class="card-container tw:mb-[0.625rem] tw:w-full"
:style="outputContainerStyle"
>
<!-- 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-h6 tw-py-2">Output Messages</div>
<div class="text-center text-h6 tw:py-2">Output Messages</div>
<q-separator class="q-mx-md q-mt-md" />
<div class="error-report-container">
<div class="text-center q-pa-md text-grey-6">
@@ -295,7 +295,7 @@ export default defineComponent({
// Custom classes
containerClass: {
type: String,
default: "tw-px-[0.625rem] tw-mb-[0.625rem] q-pt-xs",
default: "tw:px-[0.625rem] tw:mb-[0.625rem] q-pt-xs",
},
containerStyle: {
type: String,
@@ -307,11 +307,11 @@ export default defineComponent({
},
headerClass: {
type: String,
default: "tw-py-3",
default: "tw:py-3",
},
titleClass: {
type: String,
default: "tw-font-[600] tw-text-[20px]",
default: "tw:font-[600] tw:text-[20px]",
},
contentWrapperClass: {
type: String,
+2 -2
View File
@@ -15,12 +15,12 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<template>
<div class="tw-flex tw-items-center tw-gap-2">
<div class="tw:flex tw:items-center tw:gap-2">
<img v-if="showIcon" style="width: 24px; height: 24px;" :src="icon" alt="icon" />
<span class="title-text">
{{ title }}
</span>
<span class="tw-flex-1">
<span class="tw:flex-1">
<q-separator />
</span>
</div>
+9 -9
View File
@@ -9,7 +9,7 @@
{{ title }}
</div>
</div>
<div class="tw-flex tw-items-center">
<div class="tw:flex tw:items-center">
<div>
<q-btn
v-if="config.isEnterprise == 'true' && store.state.zoConfig.ai_enabled"
@@ -27,7 +27,7 @@
@mouseleave="isHovered = false"
>
<div class="row items-center no-wrap tw-gap-2 ">
<div class="row items-center no-wrap tw:gap-2 ">
<img :src="getBtnLogo" class="header-icon ai-icon" />
</div>
</q-btn>
@@ -35,16 +35,16 @@
<q-icon
v-close-popup
name="cancel"
class="cursor-pointer tw-mr-3"
class="cursor-pointer tw:mr-3"
size="20px"
data-test="json-editor-close"
/>
</div>
</div>
<q-separator></q-separator>
<div class="tw-h-[calc(100vh-65px)] tw-flex">
<div class="row common-json-editor column tw-h-[calc(100vh-65px)]"
:class="store.state.isAiChatEnabled ? 'tw-w-[64.5vw]' : 'tw-w-[90vw]'"
<div class="tw:h-[calc(100vh-65px)] tw:flex">
<div class="row common-json-editor column tw:h-[calc(100vh-65px)]"
:class="store.state.isAiChatEnabled ? 'tw:w-[64.5vw]' : 'tw:w-[90vw]'"
>
<q-card-section class="col q-pa-none">
<query-editor
@@ -78,12 +78,12 @@
<q-btn
flat
:label="t('common.cancel')"
class="q-ml-sm o2-secondary-button tw-h-[36px]"
class="q-ml-sm o2-secondary-button tw:h-[36px]"
v-close-popup
data-test="json-editor-cancel"
/>
<q-btn
class="q-ml-sm o2-primary-button tw-h-[36px]"
class="q-ml-sm o2-primary-button tw:h-[36px]"
padding="sm lg"
no-caps
:label="t('common.save')"
@@ -94,7 +94,7 @@
</div>
<!-- o2aichat enableddd -->
<div class="q-ml-sm tw-w-[25vw] tw-h-[calc(100vh - 65px)]" v-if="store.state.isAiChatEnabled " :class="store.state.theme == 'dark' ? 'dark-mode-chat-container' : 'light-mode-chat-container'" >
<div class="q-ml-sm tw:w-[25vw] tw:h-[calc(100vh - 65px)]" v-if="store.state.isAiChatEnabled " :class="store.state.theme == 'dark' ? 'dark-mode-chat-container' : 'light-mode-chat-container'" >
<O2AIChat style="height: calc(100vh - 70px) !important;" :is-open="store.state.isAiChatEnabled" @close="store.state.isAiChatEnabled = false" />
</div>
</div>
@@ -122,25 +122,25 @@ describe("LoadingProgress", () => {
it("should have correct CSS classes for dark theme", () => {
store.state.theme = "dark";
wrapper = createWrapper();
const element = wrapper.find('.tw-relative.tw-overflow-x-hidden');
expect(element.classes()).toContain('tw-bg-gray-700');
const element = wrapper.find('.tw\\:relative.tw\\:overflow-x-hidden');
expect(element.classes()).toContain('tw:bg-gray-700');
});
it("should have correct CSS classes for light theme", () => {
store.state.theme = "light";
wrapper = createWrapper();
const element = wrapper.find('.tw-relative.tw-overflow-x-hidden');
expect(element.classes()).toContain('tw-bg-gray-200');
const element = wrapper.find('.tw\\:relative.tw\\:overflow-x-hidden');
expect(element.classes()).toContain('tw:bg-gray-200');
});
it("should show opacity 100 when loading", () => {
wrapper = createWrapper({ loading: true });
expect(wrapper.classes()).toContain('tw-opacity-100');
expect(wrapper.classes()).toContain('tw:opacity-100');
});
it("should show opacity 0 when not loading and not fading out", () => {
wrapper = createWrapper({ loading: false });
expect(wrapper.classes()).toContain('tw-opacity-0');
expect(wrapper.classes()).toContain('tw:opacity-0');
});
it("should expose correct properties from setup", () => {
+16 -16
View File
@@ -1,21 +1,21 @@
<template>
<div
class="tw-absolute tw-top-0 tw-left-0 tw-w-full tw-z-[999] tw-transition-opacity tw-duration-500 tw-ease-out"
class="tw:absolute tw:top-0 tw:left-0 tw:w-full tw:z-[999] tw:transition-opacity tw:duration-500 tw:ease-out"
:class="{
'tw-opacity-0': !loading && !isFadingOut,
'tw-opacity-100': loading || isFadingOut,
'tw:opacity-0': !loading && !isFadingOut,
'tw:opacity-100': loading || isFadingOut,
}"
>
<div
class="tw-w-full tw-h-[2px] tw-relative tw-overflow-x-hidden"
class="tw:w-full tw:h-[2px] tw:relative tw:overflow-x-hidden"
:class="
store.state.theme === 'dark' ? 'tw-bg-gray-700' : 'tw-bg-gray-200'
store.state.theme === 'dark' ? 'tw:bg-gray-700' : 'tw:bg-gray-200'
"
>
<div
class="tw-h-full tw-relative tw-overflow-hidden"
class="tw:h-full tw:relative tw:overflow-hidden"
:class="
store.state.theme === 'dark' ? 'tw-bg-[#5960B2]' : 'tw-bg-[#5960B2]'
store.state.theme === 'dark' ? 'tw:bg-[#5960B2]' : 'tw:bg-[#5960B2]'
"
:style="{
width: `${displayPercentage}%`,
@@ -25,11 +25,11 @@
}"
>
<div
class="tw-absolute tw-inset-0 tw-bg-gradient-to-r tw-from-transparent tw-to-transparent "
class="tw:absolute tw:inset-0 tw:bg-gradient-to-r tw:from-transparent tw:to-transparent "
:class="
store.state.theme === 'dark'
? 'tw-via-gray-300/40'
: 'tw-via-white/40'
? 'tw:via-gray-300/40'
: 'tw:via-white/40'
"
:style="{
animation: 'shimmer 1.5s infinite linear',
@@ -40,9 +40,9 @@
</div>
<!-- Moving circle indicator -->
<div
class="tw-absolute tw-top-0 tw-w-[3px] tw-h-[2px] tw-rounded-full tw-shadow-[0_0_10px_2px_rgba(89,96,178,0.5)] tw-transform tw-translate-x-[-50%]"
class="tw:absolute tw:top-0 tw:w-[3px] tw:h-[2px] tw:rounded-full tw:shadow-[0_0_10px_2px_rgba(89,96,178,0.5)] tw:transform tw:translate-x-[-50%]"
:class="
store.state.theme === 'dark' ? 'tw-bg-[#5960B2]' : 'tw-bg-[#5960B2]'
store.state.theme === 'dark' ? 'tw:bg-[#5960B2]' : 'tw:bg-[#5960B2]'
"
:style="{
left: `${displayPercentage}%`,
@@ -52,11 +52,11 @@
}"
>
<div
class="tw-absolute tw-inset-0 tw-rounded-full tw-animate-pulse"
class="tw:absolute tw:inset-0 tw:rounded-full tw:animate-pulse"
:class="
store.state.theme === 'dark'
? 'tw-bg-gray-300/20'
: 'tw-bg-white/20'
? 'tw:bg-gray-300/20'
: 'tw:bg-white/20'
"
></div>
</div>
@@ -161,7 +161,7 @@ export default defineComponent({
}
}
.tw-animate-pulse {
.tw:animate-pulse {
animation: pulse 1.5s cubic-bezier(0.4, 0, 0.6, 1) infinite;
}
</style>
@@ -67,7 +67,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<div class="flex justify-start q-mt-sm">
<q-btn
v-close-popup="true"
class="o2-secondary-button tw-h-[36px]"
class="o2-secondary-button tw:h-[36px]"
:class="store.state.theme === 'dark' ? 'o2-secondary-button-dark' : 'o2-secondary-button-light'"
flat
:label="t('dashboard.cancel')"
@@ -79,7 +79,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
:disable="folderData.name.trim() === ''"
:loading="onSubmit.isLoading.value"
:label="t('common.save')"
class="o2-primary-button tw-h-[36px] q-ml-md"
class="o2-primary-button tw:h-[36px] q-ml-md"
:class="store.state.theme === 'dark' ? 'o2-primary-button-dark' : 'o2-primary-button-light'"
flat
type="submit"
@@ -27,7 +27,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
:pagination="{ rowsPerPage: 10000 }"
hide-header
hide-bottom
class="traces-field-table tw-h-full"
class="traces-field-table tw:h-full"
id="tracesFieldList"
>
<template #body-cell-name="props">
@@ -65,14 +65,14 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
dense
switch-toggle-side
:label="props.row.name"
expand-icon-class="field-expansion-icon !tw-text-[1rem] tw-text-[var(--o2-icon-color)]"
expand-icon-class="field-expansion-icon tw:text-[1rem]! tw:text-[var(--o2-icon-color)]"
expand-icon="
expand_more
"
@before-show="
(event: any) => openFilterCreator(event, props.row)
"
class="hover:tw-bg-[var(--o2-hover-accent)] tw-rounded-[0.25rem]"
class="hover:tw:bg-[var(--o2-hover-accent)] tw:rounded-[0.25rem]"
>
<template v-slot:header>
<div
@@ -171,7 +171,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
"
>
<q-btn
class="q-mr-xs !tw-border !tw-border-solid !tw-border-[var(--o2-border-color)]"
class="q-mr-xs tw:border! tw:border-solid! tw:border-[var(--o2-border-color)]!"
size="5px"
title="Include Term"
round
@@ -181,12 +181,12 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
)
"
>
<q-icon class="!tw-h-[0.5rem] !tw-w-[0.5rem]">
<q-icon class="tw:h-[0.5rem]! tw:w-[0.5rem]!">
<EqualIcon></EqualIcon>
</q-icon>
</q-btn>
<q-btn
class="q-mr-xs !tw-border !tw-border-solid !tw-border-[var(--o2-border-color)]"
class="q-mr-xs tw:border! tw:border-solid! tw:border-[var(--o2-border-color)]!"
size="5px"
title="Include Term"
round
@@ -196,7 +196,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
)
"
>
<q-icon class="!tw-h-[0.5rem] !tw-w-[0.5rem]">
<q-icon class="tw:h-[0.5rem]! tw:w-[0.5rem]!">
<NotEqualIcon></NotEqualIcon>
</q-icon>
</q-btn>
@@ -242,7 +242,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
clearable
debounce="1"
:placeholder="t('search.searchField')"
class="o2-search-input tw-min-w-full "
class="o2-search-input tw:min-w-full "
>
<template #prepend>
<q-icon name="search" class="o2-search-input-icon" />
@@ -17,16 +17,15 @@ 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="card-container tw-h-full tw-flex tw-flex-col tw-pb-[0.3rem]">
<div class="card-container tw:h-full tw:flex tw:flex-col tw:pb-[0.3rem]">
<div class="folder-header" :class="store.state.theme === 'dark' ? 'folder-header-dark' : 'folder-header-light'">
<div class="text-bold q-px-sm q-py-sm tw-flex tw-items-center tw-justify-between tw-gap-2">
<div class="text-bold q-px-sm q-py-sm tw:flex tw:items-center tw:justify-between tw:gap-2">
Folders
<div>
<q-btn
class="text-bold o2-secondary-button tw-h-[28px] tw-w-[32px]"
class="text-bold o2-secondary-button tw:h-[28px] tw:w-[32px] tw:min-w-[32px]!"
:class="store.state.theme === 'dark' ? 'o2-secondary-button-dark' : 'o2-secondary-button-light'"
no-caps
style="min-width: 0px !important; min-height: 0px !important;"
flat
@click.stop="addFolder"
data-test="dashboard-new-folder-btn"
@@ -36,7 +35,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
</q-btn>
</div>
</div>
<q-separator class="tw-mb-1 tw-mt-[3px]" size="2px"></q-separator>
<q-separator class="tw:mb-1 tw:mt-[3px]" size="2px"></q-separator>
<!-- Search Input -->
<div style="width: 100%;" class="flex folder-item q-py-xs">
@@ -48,7 +47,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
placeholder="Search Folder"
style="width: 100%;"
clearable
class="tw-mx-2 q-px-xs"
class="tw:mx-2 q-px-xs"
:class="store.state.theme === 'dark' ? 'o2-search-input-dark' : 'o2-search-input-light'"
>
<template #prepend>
@@ -57,7 +56,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
</q-input>
</div>
</div>
<div class="folders-tabs tw-flex-1 tw-overflow-y-auto">
<div class="folders-tabs tw:flex-1 tw:overflow-y-auto">
<q-tabs
indicator-color="transparent"
inline-label
@@ -26,7 +26,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
behavior="menu"
borderless
dense
class="showLabelOnTop no-case tw-mr-1"
class="showLabelOnTop no-case tw:mr-1"
style="width: calc(100% - 44px)"
:disable="disableDropdown"
>
@@ -86,8 +86,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
:label="t('dashboard.cancel')"
no-caps
flat
dense
class="o2-secondary-button tw-h-[36px]"
class="o2-secondary-button tw:h-[36px]"
:class="store.state.theme === 'dark' ? 'o2-secondary-button-dark' : 'o2-secondary-button-light'"
data-test="dashboard-add-cancel"
/>
@@ -96,9 +95,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
:disable="dashboardData.name.trim() === ''"
:loading="onSubmit.isLoading.value"
:label="t('dashboard.save')"
dense
flat
class="o2-primary-button tw-h-[36px] q-ml-md"
class="o2-primary-button tw:h-[36px] q-ml-md"
:class="store.state.theme === 'dark' ? 'o2-primary-button-dark' : 'o2-primary-button-light'"
type="submit"
no-caps
+2 -2
View File
@@ -69,7 +69,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<div class="flex justify-start q-mt-sm">
<q-btn
v-close-popup="true"
class="o2-secondary-button tw-h-[36px]"
class="o2-secondary-button tw:h-[36px]"
:class="store.state.theme === 'dark' ? 'o2-secondary-button-dark' : 'o2-secondary-button-light'"
flat
:label="t('dashboard.cancel')"
@@ -81,7 +81,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
:disable="folderData.name.trim() === ''"
:loading="onSubmit.isLoading.value"
:label="t('dashboard.save')"
class="o2-primary-button tw-h-[36px] q-ml-md"
class="o2-primary-button tw:h-[36px] q-ml-md"
:class="store.state.theme === 'dark' ? 'o2-primary-button-dark' : 'o2-primary-button-light'"
flat
type="submit"
@@ -66,7 +66,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<div class="flex justify-start q-mt-sm">
<q-btn
v-close-popup="true"
class="o2-secondary-button tw-h-[36px]"
class="o2-secondary-button tw:h-[36px]"
:class="store.state.theme === 'dark' ? 'o2-secondary-button-dark' : 'o2-secondary-button-light'"
flat
:label="t('dashboard.cancel')"
@@ -78,7 +78,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
:disable="activeFolderId === selectedFolder.value"
:loading="onSubmit.isLoading.value"
:label="t('common.move')"
class="o2-primary-button tw-h-[36px] q-ml-md"
class="o2-primary-button tw:h-[36px] q-ml-md"
:class="store.state.theme === 'dark' ? 'o2-primary-button-dark' : 'o2-primary-button-light'"
flat
type="submit"
@@ -22,7 +22,7 @@
<div
v-for="(overrideConfig, index) in overrideConfigs"
:key="index"
class="q-mb-md flex items-start tw-w-full tw-flex"
class="q-mb-md flex items-start tw:w-full tw:flex"
style="gap: 15px"
>
<q-select
@@ -37,9 +37,9 @@
map-options
borderless
dense
class="tw-flex-1 o2-custom-select-dashboard"
class="tw:flex-1 o2-custom-select-dashboard"
hide-bottom-space/>
<div class="tw-flex items-center" style="width: 60%; gap: 10px">
<div class="tw:flex items-center" style="width: 60%; gap: 10px">
<q-select
v-model="overrideConfig.config[0].type"
:label="'Type'"
@@ -58,7 +58,7 @@
<div
v-if="overrideConfig.config[0].type === 'unit'"
class="tw-flex items-center"
class="tw:flex items-center"
style="gap: 10px; flex-grow: 1; width: 60%"
>
<q-select
@@ -73,7 +73,7 @@
map-options
borderless
dense
class="tw-flex-1 o2-custom-select-dashboard"
class="tw:flex-1 o2-custom-select-dashboard"
hide-bottom-space/>
<q-input
v-if="overrideConfig.config[0].value.unit === 'custom'"
@@ -91,7 +91,7 @@
<div
v-else-if="overrideConfig.config[0].type === 'unique_value_color'"
class="tw-flex items-center"
class="tw:flex items-center"
style="gap: 10px; flex-grow: 1; width: 60%"
>
<q-checkbox
@@ -26,7 +26,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
behavior="menu"
borderless
dense
class="showLabelOnTop no-case tw-mr-1 tw-mt-[1px] o2-custom-select-dashboard"
class="showLabelOnTop no-case tw:mr-1 tw:mt-[1px] o2-custom-select-dashboard"
style="width: calc(100% - 44px)"
>
<template #no-option>
@@ -81,19 +81,19 @@
</div>
</q-card-section>
<q-card-actions align="right">
<div class="tw-w-full tw-flex">
<div class="tw:w-full tw:flex">
<q-btn
v-if="annotationData.annotation_id"
color="negative"
label="Delete"
@click="handleDeleteWithConfirm"
/>
<div class="tw-flex-1"></div>
<div class="tw:flex-1"></div>
<q-btn
flat
label="Cancel"
@click="handleClose"
class="o2-secondary-button tw-h-[36px] q-ml-md"
class="o2-secondary-button tw:h-[36px] q-ml-md"
:class="
store.state.theme === 'dark'
? 'o2-secondary-button-dark'
@@ -101,7 +101,7 @@
"
/>
<q-btn
class="o2-primary-button tw-h-[36px] q-ml-md"
class="o2-primary-button tw:h-[36px] q-ml-md"
:class="
store.state.theme === 'dark'
? 'o2-primary-button-dark'
@@ -6,7 +6,7 @@
:options="colorModeOptions"
dense
:label="t('dashboard.colorMode')"
class="showLabelOnTop selectedLabel tw-w-full"
class="showLabelOnTop selectedLabel tw:w-full"
stack-label
emit-value
:display-value="
@@ -21,7 +21,7 @@
style="padding: 5px 10px; min-width: min(1200px, 90vw)"
>
<div
class="flex justify-between items-center q-pa-md header tw-top-0 tw-sticky"
class="flex justify-between items-center q-pa-md header tw:top-0 tw:sticky"
style="border-bottom: 2px solid gray; margin-bottom: 5px"
>
<div class="flex items-center q-table__title q-mr-md">
@@ -39,8 +39,8 @@
data-test="dashboard-color-by-series-cancel"
></q-btn>
</div>
<div class="flex tw-justify-between tw-flex-col">
<div class="tw-mt-2 scrollable-content scroll tw-min-h-52">
<div class="flex tw:justify-between tw:flex-col">
<div class="tw:mt-2 scrollable-content scroll tw:min-h-52">
<draggable
v-model="editColorBySeries"
:options="dragOptions"
@@ -52,7 +52,7 @@
:key="index"
class="draggable-row"
>
<div class="draggable-handle tw-self-center">
<div class="draggable-handle tw:self-center">
<q-icon
name="drag_indicator"
color="grey-13"
@@ -60,8 +60,8 @@
:data-test="`dashboard-addpanel-config-color-by-series-drag-handle-${index}`"
/>
</div>
<div class="draggable-content tw-flex tw-gap-x-6">
<div class="input-container tw-flex-1">
<div class="draggable-content tw:flex tw:gap-x-6">
<div class="input-container tw:flex-1">
<CommonAutoComplete
v-model="series.value"
:items="seriesDataItems"
@@ -89,10 +89,10 @@
</div>
<!-- Color Picker -->
<div class="color-section tw-flex-1">
<div class="color-section tw:flex-1">
<div
v-if="series.color !== null"
class="tw-items-center tw-flex"
class="tw:items-center tw:flex"
>
<q-input
v-model="series.color"
@@ -116,19 +116,19 @@
<q-icon
:name="outlinedCancel"
style="width: 10%"
class="cursor-pointer tw-align-middle"
class="cursor-pointer tw:align-middle"
size="xs"
title="Remove color"
@click="removeColorByIndex(index)"
/>
</div>
<div v-else class="tw-w-full">
<div v-else class="tw:w-full">
<q-btn
label="Set color"
no-caps
flat
dense
class="tw-text-blue-700 tw-font-semibold tw-w-full"
class="tw:text-blue-700 tw:font-semibold tw:w-full"
@click="setColorByIndex(index)"
/>
</div>
@@ -150,7 +150,7 @@
</div>
<!-- Footer Buttons -->
</div>
<div class="flex justify-between tw-sticky tw-bottom-0 sticky-footer">
<div class="flex justify-between tw:sticky tw:bottom-0 sticky-footer">
<q-btn
@click="addcolorBySeries"
label="+ Add a new color"
@@ -214,7 +214,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
v-model="dashboardPanelData.data.config.trellis.group_by_y_axis"
:label="t('dashboard.groupMultiYAxisTrellis')"
data-test="dashboard-config-trellis-group-by-y-axis"
class="tw-h-[36px] -tw-ml-3 o2-toggle-button-lg"
class="tw:h-[36px] -tw:ml-3 o2-toggle-button-lg"
size="lg"
:class="
store.state.theme === 'dark'
@@ -255,7 +255,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
v-model="dashboardPanelData.data.config.show_legends"
:label="t('dashboard.showLegendsLabel')"
data-test="dashboard-config-show-legend"
class="tw-h-[36px] -tw-ml-3 o2-toggle-button-lg"
class="tw:h-[36px] -tw:ml-3 o2-toggle-button-lg"
size="lg"
:class="
store.state.theme === 'dark'
@@ -271,7 +271,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
v-model="dashboardPanelData.data.config.wrap_table_cells"
:label="t('dashboard.wraptext')"
data-test="dashboard-config-wrap-table-cells"
class="tw-h-[36px] -tw-ml-2 o2-toggle-button-lg"
class="tw:h-[36px] -tw:ml-2 o2-toggle-button-lg"
size="lg"
:class="
store.state.theme === 'dark'
@@ -287,7 +287,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
v-model="dashboardPanelData.data.config.table_transpose"
:label="t('dashboard.tableTranspose')"
data-test="dashboard-config-table_transpose"
class="tw-h-[36px] -tw-ml-2 o2-toggle-button-lg"
class="tw:h-[36px] -tw:ml-2 o2-toggle-button-lg"
size="lg"
:class="
store.state.theme === 'dark'
@@ -303,7 +303,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
v-model="dashboardPanelData.data.config.table_dynamic_columns"
:label="t('dashboard.tableDynamicColumns')"
data-test="dashboard-config-table_dynamic_columns"
class="tw-h-[36px] -tw-ml-2 o2-toggle-button-lg"
class="tw:h-[36px] -tw:ml-2 o2-toggle-button-lg"
size="lg"
:class="
store.state.theme === 'dark'
@@ -938,7 +938,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
dashboardPanelData.layout.currentQueryIndex
].fields?.breakdown?.length == 0
"
class="tw-h-[36px] -tw-ml-3 o2-toggle-button-lg"
class="tw:h-[36px] -tw:ml-3 o2-toggle-button-lg"
size="lg"
:class="
store.state.theme === 'dark'
@@ -1023,7 +1023,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
v-model="dashboardPanelData.data.config.connect_nulls"
:label="t('dashboard.connectNullValues')"
data-test="dashboard-config-connect-null-values"
class="tw-h-[36px] -tw-ml-3 o2-toggle-button-lg"
class="tw:h-[36px] -tw:ml-3 o2-toggle-button-lg"
size="lg"
:class="
store.state.theme === 'dark'
@@ -1243,7 +1243,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
v-model="dashboardPanelData.data.config.axis_border_show"
:label="t('dashboard.showBorder')"
data-test="dashboard-config-axis-border"
class="tw-h-[36px] -tw-ml-3 o2-toggle-button-lg"
class="tw:h-[36px] -tw:ml-3 o2-toggle-button-lg"
size="lg"
:class="
store.state.theme === 'dark'
@@ -1397,7 +1397,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
v-model="dashboardPanelData.data.config.show_gridlines"
:label="t('dashboard.showGridlines')"
data-test="dashboard-config-show-gridlines"
class="tw-h-[36px] -tw-ml-3 o2-toggle-button-lg"
class="tw:h-[36px] -tw:ml-3 o2-toggle-button-lg"
size="lg"
:class="
store.state.theme === 'dark'
@@ -359,16 +359,16 @@ describe("DashboardErrors", () => {
it("should have list with correct Tailwind classes", () => {
const list = wrapper.find("ul");
expect(list.exists()).toBeTruthy();
expect(list.classes()).toContain("tw-list-disc");
expect(list.classes()).toContain("tw-list-inside");
expect(list.classes()).toContain("tw-px-3");
expect(list.classes()).toContain("tw:list-disc");
expect(list.classes()).toContain("tw:list-inside");
expect(list.classes()).toContain("tw:px-3");
});
it("should style list items with red color and padding", () => {
const listItems = wrapper.findAll("li");
listItems.forEach(item => {
expect(item.attributes("style")).toContain("color: red");
expect(item.classes()).toContain("tw-py-1");
expect(item.classes()).toContain("tw:py-1");
});
});
@@ -40,12 +40,12 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
>
<div class="col">
<div data-test="dashboard-error">
<ul class="tw-list-disc tw-list-inside tw-px-3">
<ul class="tw:list-disc tw:list-inside tw:px-3">
<li
v-for="(item, index) in props.errors.errors"
:key="index"
style="color: red"
class="tw-py-1"
class="tw:py-1"
>
{{ item }}
</li>
@@ -123,7 +123,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
title="Toggle Function Editor"
@update:model-value="onFunctionToggle"
:disable="promqlMode"
class="float-left tw-h-[36px] o2-toggle-button-xs tw-mt-2"
class="float-left tw:h-[36px] o2-toggle-button-xs tw:mt-2"
size="xs"
:class="
store.state.theme === 'dark'
@@ -163,7 +163,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<template #before>
<QueryEditor
ref="queryEditorRef"
class="monaco-editor !tw-h-full"
class="monaco-editor tw:h-full!"
style="width: 100%"
v-model:query="
dashboardPanelData.data.queries[
@@ -305,7 +305,7 @@
left-label
v-model="drilldownData.data.passAllVariables"
data-test="dashboard-drilldown-pass-all-variables"
class="tw-h-[36px] tw-ml-1 o2-toggle-button-lg"
class="tw:h-[36px] tw:ml-1 o2-toggle-button-lg"
size="lg"
:class="store.state.theme === 'dark' ? 'o2-toggle-button-lg-dark' : 'o2-toggle-button-lg-light'"
/>
@@ -319,7 +319,7 @@
left-label
v-model="drilldownData.targetBlank"
data-test="dashboard-drilldown-open-in-new-tab"
class="tw-h-[36px] tw-ml-1 o2-toggle-button-lg"
class="tw:h-[36px] tw:ml-1 o2-toggle-button-lg"
size="lg"
:class="store.state.theme === 'dark' ? 'o2-toggle-button-lg-dark' : 'o2-toggle-button-lg-light'"
/>
@@ -329,7 +329,7 @@
<q-btn
unelevated
no-caps
class="o2-secondary-button tw-h-[36px] el-border"
class="o2-secondary-button tw:h-[36px] el-border"
:class="store.state.theme === 'dark' ? 'o2-secondary-button-dark' : 'o2-secondary-button-light'"
flat
@click="$emit('close')"
@@ -340,7 +340,7 @@
<q-btn
unelevated
no-caps
class="o2-primary-button tw-h-[36px] q-ml-md el-border"
class="o2-primary-button tw:h-[36px] q-ml-md el-border"
:class="store.state.theme === 'dark' ? 'o2-primary-button-dark' : 'o2-primary-button-light'"
flat
@click="saveDrilldown"
@@ -19,7 +19,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
class="column index-menu"
:class="store.state.theme == 'dark' ? 'theme-dark' : 'theme-light'"
>
<div class="col-auto tw-mx-[0.625rem]">
<div class="col-auto tw:mx-[0.625rem]">
<!-- stream type selection will be hidden for metrics page -->
<q-select
v-if="dashboardPanelDataPageKey !== 'metrics'"
@@ -169,7 +169,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
>
<div
v-if="props?.row?.isGroup"
class="tw-pl-2 tw-py-1 tw-font-semibold tw-bg-gray-200"
class="tw:pl-2 tw:py-1 tw:font-semibold tw:bg-gray-200"
>
{{ props?.row?.groupName }}
</div>
@@ -523,7 +523,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
debounce="1"
:loading="getStreamFields.isLoading.value"
:placeholder="t('search.searchField')"
class="tw-mx-[0.625rem]"
class="tw:mx-[0.625rem]"
hide-bottom-space>
<template #prepend>
<q-icon name="search" />
@@ -66,12 +66,12 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
placeholder="latitude or lat"
borderless
dense
class="tw-mb-3 showLabelOnTop"
class="tw:mb-3 showLabelOnTop"
stack-label
data-test="dashboard-config-geo-lat-label"
>
<template v-slot:label>
<div class="row items-center all-pointer-events tw-mb-[-5px]">
<div class="row items-center all-pointer-events tw:mb-[-5px]">
{{ t("dashboard.geoLatLabel") }}
<q-icon class="q-ml-xs" size="20px" name="info" />
<q-tooltip class="bg-grey-8" max-width="250px">
@@ -88,12 +88,12 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
placeholder="longitude or lon"
borderless
dense
class="tw-mb-3 showLabelOnTop"
class="tw:mb-3 showLabelOnTop"
stack-label
data-test="dashboard-config-geo-lon-label"
>
<template v-slot:label>
<div class="row items-center all-pointer-events tw-mb-[-5px]">
<div class="row items-center all-pointer-events tw:mb-[-5px]">
{{ t("dashboard.geoLonLabel") }}
<q-icon class="q-ml-xs" size="20px" name="info" />
<q-tooltip class="bg-grey-8" max-width="250px">
@@ -110,12 +110,12 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
placeholder="weight"
borderless
dense
class="tw-mb-3 showLabelOnTop"
class="tw:mb-3 showLabelOnTop"
stack-label
data-test="dashboard-config-geo-weight-label"
>
<template v-slot:label>
<div class="row items-center all-pointer-events tw-mb-[-5px]">
<div class="row items-center all-pointer-events tw:mb-[-5px]">
{{ t("dashboard.geoWeightLabel") }}
<q-icon class="q-ml-xs" size="20px" name="info" />
<q-tooltip class="bg-grey-8" max-width="250px">
@@ -140,7 +140,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
data-test="dashboard-config-maps-name-label"
>
<template v-slot:label>
<div class="row items-center all-pointer-events tw-mb-[-5px]">
<div class="row items-center all-pointer-events tw:mb-[-5px]">
{{ t("dashboard.mapsNameLabel") }}
<q-icon class="q-ml-xs" size="20px" name="info" />
<q-tooltip class="bg-grey-8" max-width="300px">
@@ -162,7 +162,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
data-test="dashboard-config-maps-type"
>
<template v-slot:label>
<div class="row items-center all-pointer-events tw-mb-[-5px]">
<div class="row items-center all-pointer-events tw:mb-[-5px]">
{{ t("dashboard.mapsMapType") }}
<q-icon class="q-ml-xs" size="20px" name="info" />
<q-tooltip class="bg-grey-8" max-width="250px">
@@ -283,7 +283,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
:display-value="getVisibleColumnsDisplay"
>
<template v-slot:label>
<div class="row items-center all-pointer-events tw-mb-[-5px]">
<div class="row items-center all-pointer-events tw:mb-[-5px]">
{{ t("dashboard.visibleColumns") }}
<q-icon class="q-ml-xs" size="18px" name="info">
<q-tooltip class="bg-grey-8" max-width="400px">
@@ -339,7 +339,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
:display-value="getHiddenColumnsDisplay"
>
<template v-slot:label>
<div class="row items-center all-pointer-events tw-mb-[-5px]">
<div class="row items-center all-pointer-events tw:mb-[-5px]">
{{ t("dashboard.hiddenColumns") }}
<q-icon class="q-ml-xs" size="18px" name="info">
<q-tooltip class="bg-grey-8" max-width="400px">
@@ -391,7 +391,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
data-test="dashboard-config-sticky-first-column"
>
<template v-slot:default>
<div class="row items-center all-pointer-events tw-mb-[-5px]">
<div class="row items-center all-pointer-events tw:mb-[-5px]">
{{ t("dashboard.stickyFirstColumn") }}
<q-icon class="q-ml-xs" size="20px" name="info" />
<q-tooltip class="bg-grey-8" max-width="300px">
@@ -61,7 +61,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
</button>
</div>
</div>
<div class="row button-group tw-ml-[0.25rem]">
<div class="row button-group tw:ml-[0.25rem]">
<div v-if="dashboardPanelData.data.type != 'custom_chart'">
<button
data-test="dashboard-builder-query-type"
@@ -23,7 +23,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<q-card-section>
<!-- Header -->
<div
class="flex justify-between items-center q-px-md q-py-sm header tw-top-0 tw-sticky"
class="flex justify-between items-center q-px-md q-py-sm header tw:top-0 tw:sticky"
style="margin-bottom: 5px"
>
<div class="flex items-center q-table__title q-mr-md">
@@ -36,7 +36,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<q-btn
:icon="isAllCopied ? 'check' : 'content_copy'"
:label="isAllCopied ? 'Copied' : 'Copy all'"
class="q-px-sm q-mr-sm tw-border tw-border-solid tw-border-[var(--o2-border-color)] tw-font-normal"
class="q-px-sm q-mr-sm tw:border tw:border-solid tw:border-[var(--o2-border-color)] tw:font-normal"
no-caps
dense
size="sm"
@@ -82,7 +82,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
dense
size="xs"
no-caps
class="copy-btn q-ml-sm tw-font-normal"
class="copy-btn q-ml-sm tw:font-normal"
@click.stop="copyLegend(legend.name, index)"
>
<q-tooltip>{{
@@ -2,20 +2,20 @@
Sort By:
<q-btn-group class="el-border">
<q-btn
:class="[!fieldObj.sortBy ? 'selected' : '', 'tw-px-2.5', 'el-border']"
:class="[!fieldObj.sortBy ? 'selected' : '', 'tw:px-2.5', 'el-border']"
@click="updateSortOption(null)"
icon="block"
size="sm"
data-test="dashboard-sort-by-item-clear"
/>
<q-btn
:class="[fieldObj.sortBy === 'ASC' ? 'selected' : '', 'tw-px-2', 'custom-border']"
:class="[fieldObj.sortBy === 'ASC' ? 'selected' : '', 'tw:px-2', 'custom-border']"
@click="updateSortOption('ASC')"
data-test="dashboard-sort-by-item-asc"
><AscSort
/></q-btn>
<q-btn
:class="[fieldObj.sortBy === 'DESC' ? 'selected' : '', 'tw-px-2.5', 'el-border']"
:class="[fieldObj.sortBy === 'DESC' ? 'selected' : '', 'tw:px-2.5', 'el-border']"
@click="updateSortOption('DESC')"
data-test="dashboard-sort-by-item-desc"
><DescSort
@@ -39,7 +39,7 @@
data-test="dashboard-tab-settings-tab-name-edit-cancel"
></q-btn>
</div>
<div class="tw-mb-4">
<div class="tw:mb-4">
<draggable
v-model="editedValueMapping"
:options="dragOptions"
@@ -51,7 +51,7 @@
:key="index"
class="draggable-row"
>
<div class="draggable-handle tw-self-center">
<div class="draggable-handle tw:self-center">
<q-icon
name="drag_indicator"
color="grey-13"
@@ -59,7 +59,7 @@
:data-test="`dashboard-addpanel-config-value-mapping-drag-handle-${index}`"
/>
</div>
<div class="draggable-content tw-flex tw-gap-x-6">
<div class="draggable-content tw:flex tw:gap-x-6">
<q-select
v-model="mapping.type"
label="Type"
@@ -71,11 +71,11 @@
behavior="menu"
borderless
dense
class="q-mb-xs tw-flex-1 o2-custom-select-dashboard"
class="q-mb-xs tw:flex-1 o2-custom-select-dashboard"
hide-bottom-space></q-select>
<div
v-if="mapping.type === 'value'"
class="input-container tw-flex-1"
class="input-container tw:flex-1"
>
<q-input
v-model="mapping.value"
@@ -87,7 +87,7 @@
</div>
<div
v-if="mapping.type === 'regex'"
class="input-container tw-flex-1"
class="input-container tw:flex-1"
>
<q-input
v-model="mapping.pattern"
@@ -99,7 +99,7 @@
</div>
<div
v-if="mapping.type === 'range'"
class="input-container tw-flex-1"
class="input-container tw:flex-1"
>
<q-input
v-model="mapping.from"
@@ -111,7 +111,7 @@
<q-input
v-model="mapping.to"
label="To"
class="input-spacing tw-flex-1"
class="input-spacing tw:flex-1"
dense
:data-test="`dashboard-addpanel-config-value-mapping-to-input-${index}`"
borderless hide-bottom-space/>
@@ -119,14 +119,14 @@
<q-input
v-model="mapping.text"
label="Display Value"
class="input-spacing tw-flex-1"
class="input-spacing tw:flex-1"
dense
:data-test="`dashboard-addpanel-config-value-mapping-text-input-${index}`"
borderless hide-bottom-space/>
<div class="color-section tw-flex-1">
<div class="color-section tw:flex-1">
<div
v-if="mapping.color !== null"
class="tw-items-center tw-flex"
class="tw:items-center tw:flex"
>
<q-input
v-model="mapping.color"
@@ -145,19 +145,19 @@
<q-icon
:name="outlinedCancel"
style="width: 10%"
class="cursor-pointer tw-align-middle"
class="cursor-pointer tw:align-middle"
size="xs"
title="Remove color"
@click="removeColorByIndex(index)"
/>
</div>
<div v-else class="tw-w-full">
<div v-else class="tw:w-full">
<q-btn
label="Set color"
no-caps
flat
dense
class="tw-text-blue-700 tw-font-semibold tw-w-full"
class="tw:text-blue-700 tw:font-semibold tw:w-full"
@click="setColorByIndex(index)"
/>
</div>
@@ -20,20 +20,20 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
data-test="dialog-box"
:class="
warningMessage && warningMessage.length > 0
? 'tw-w-[600px]'
: 'tw-w-[400px]'
? 'tw:w-[600px]'
: 'tw:w-[400px]'
"
>
<q-card-section class="confirmBody">
<div class="head">{{ title }}</div>
<div class="para">{{ message }}</div>
<div v-if="warningMessage && warningMessage.length > 0" class="tw-mt-4">
<div v-if="warningMessage && warningMessage.length > 0" class="tw:mt-4">
<q-banner
:class="[
'tw-border-l-4 tw-p-4 tw-rounded',
'tw:border-l-4 tw:p-4 tw:rounded',
store.state.theme === 'dark'
? 'tw-bg-gray-800/60 tw-border-yellow-600/70'
: 'tw-bg-orange-50 tw-border-orange-400',
? 'tw:bg-gray-800/60 tw:border-yellow-600/70'
: 'tw:bg-orange-50 tw:border-orange-400',
]"
>
<template v-slot:avatar>
@@ -41,41 +41,41 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
name="warning"
:class="
store.state.theme === 'dark'
? 'tw-text-yellow-500/80'
: 'tw-text-orange-500'
? 'tw:text-yellow-500/80'
: 'tw:text-orange-500'
"
size="24px"
/>
</template>
<div
:class="[
'tw-font-medium tw-text-sm tw-leading-relaxed tw-text-left',
'tw:font-medium tw:text-sm tw:leading-relaxed tw:text-left',
store.state.theme === 'dark'
? 'tw-text-gray-300'
: 'tw-text-orange-800',
? 'tw:text-gray-300'
: 'tw:text-orange-800',
]"
>
{{ warningMessage }}
</div>
</q-banner>
</div>
<div v-if="hasQuery" class="tw-mt-4">
<div v-if="hasQuery" class="tw:mt-4">
<q-checkbox
v-model="replaceQuery"
label="Also replace query with example query"
data-test="replace-query-checkbox"
:class="
store.state.theme === 'dark'
? 'tw-text-gray-300'
: 'tw-text-gray-700'
? 'tw:text-gray-300'
: 'tw:text-gray-700'
"
/>
<div
class="tw-text-xs tw-mt-1 tw-ml-7"
class="tw:text-xs tw:mt-1 tw:ml-7"
:class="
store.state.theme === 'dark'
? 'tw-text-gray-400'
: 'tw-text-gray-500'
? 'tw:text-gray-400'
: 'tw:text-gray-500'
"
>
The example query will be inserted into the query editor
@@ -631,7 +631,7 @@ describe("DynamicFunctionPopUp", () => {
describe("Layout and Styling", () => {
it("should have flex layout", () => {
wrapper = createWrapper();
const container = wrapper.find('div.tw-flex');
const container = wrapper.find('div.tw\\:flex');
expect(container.exists()).toBe(true);
});
@@ -1,10 +1,10 @@
<template>
<div class="tw-flex tw-gap-2">
<div class="tw:flex tw:gap-2">
<div style="width: 134px; padding-right: 12px; padding-top: 12px">
<div class="text-label-bold tw-pb-3">Property</div>
<div class="text-label-bold tw:pb-3">Property</div>
<div style="display: flex; flex-direction: column; gap: 14px">
<div>
<div class="text-label-normal tw-text-sm">Label</div>
<div class="text-label-normal tw:text-sm">Label</div>
<input
v-model="fields.label"
:class="[
@@ -15,7 +15,7 @@
/>
</div>
<div>
<div class="text-label-normal tw-text-sm">Alias</div>
<div class="text-label-normal tw:text-sm">Alias</div>
<input
v-model="fields.alias"
disabled
@@ -63,7 +63,7 @@
<q-tab-panel name="build" style="padding: 0px; padding-top: 8px">
<div style="display: flex">
<div style="width: calc(100% - 134px)">
<div class="text-label-bold tw-pb-3">Configuration</div>
<div class="text-label-bold tw:pb-3">Configuration</div>
<SelectFunction
v-model="fields"
data-test="dynamic-function-popup-select-function"
@@ -84,9 +84,9 @@
</q-tab-panel>
</q-tab-panels>
<div class="tw-pt-2 tw-pr-3" v-if="allowAggregation">
<div class="tw-flex tw-items-center tw-gap-2 tw-mb-2">
<span class="tw-font-bold">Having</span>
<div class="tw:pt-2 tw:pr-3" v-if="allowAggregation">
<div class="tw:flex tw:items-center tw:gap-2 tw:mb-2">
<span class="tw:font-bold">Having</span>
<q-btn
dense
@@ -103,7 +103,7 @@
</div>
<div
class="tw-flex tw-space-x-2 tw-items-center"
class="tw:flex tw:space-x-2 tw:items-center"
v-if="isHavingFilterVisible()"
>
<q-select
@@ -2,7 +2,7 @@
<div style="width: 100%">
<div class="query-section">
<div class="query-label">Query</div>
<div class="query-label tw-text-xs">
<div class="query-label tw:text-xs">
Write a SQL query for complex actions.
</div>
@@ -17,27 +17,27 @@
option-value="value"
emit-value
map-options
class="tw-w-72 o2-custom-select-dashboard"
class="tw:w-72 o2-custom-select-dashboard"
>
</q-select>
<div class="tw-w-full tw-p-3 tw-flex tw-gap-2">
<div class="tw:w-full tw:p-3 tw:flex tw:gap-2">
<!-- Loop through the args for the first n-1 arguments -->
<div class="tw-w-full">
<div class="tw:w-full">
<div
v-for="(arg, argIndex) in fields.args"
:key="argIndex + '-' + arg.type"
class="tw-w-full tw-flex tw-flex-col"
class="tw:w-full tw:flex tw:flex-col"
>
<div
class="tw-flex"
class="tw:flex"
:style="{
marginLeft: isChild ? '-60px' : '0px',
}"
>
<div class="tw-mr-2 tw-relative" style="min-height: 50px">
<div class="tw:mr-2 tw:relative" style="min-height: 50px">
<!-- Vertical Line using top & bottom instead of height -->
<div
class="tw-absolute tw-top-0 tw-w-[1px] tw-bg-[#001495] tw-opacity-50"
class="tw:absolute tw:top-0 tw:w-[1px] tw:bg-[#001495] tw:opacity-50"
:style="{
bottom:
argIndex === fields.args.length - 1
@@ -48,18 +48,18 @@
></div>
<!-- SubTask Arrow -->
<div class="tw-absolute" style="top: 30px; left: -1px">
<div class="tw:absolute" style="top: 30px; left: -1px">
<SubTaskArrow />
</div>
</div>
<div>
<div class="tw-flex tw-items-center tw-gap-x-2">
<div class="tw:flex tw:items-center tw:gap-x-2">
<label :for="'arg-' + argIndex">{{
getParameterLabel(fields.functionName, argIndex)
}}</label>
</div>
<div class="tw-flex">
<div class="tw:flex">
<!-- type selector -->
<q-select
v-model="fields.args[argIndex].type"
@@ -78,7 +78,7 @@
dense
filled
:display-value="''"
class="o2-custom-select-dashboard arg-type-select tw-mr-0.5"
class="o2-custom-select-dashboard arg-type-select tw:mr-0.5"
:required="isRequired(fields.functionName, argIndex)"
:data-test="`dashboard-function-dropdown-arg-type-selector-${argIndex}`"
>
@@ -91,7 +91,7 @@
</q-select>
<!-- Left field selector using StreamFieldSelect -->
<div
class="tw-w-52"
class="tw:w-52"
v-if="fields.args[argIndex]?.type === 'field'"
>
<StreamFieldSelect
@@ -107,7 +107,7 @@
v-model="fields.args[argIndex].value"
placeholder="Enter string"
:required="isRequired(fields.functionName, argIndex)"
class="tw-w-52"
class="tw:w-52"
dense
:data-test="`dashboard-function-dropdown-arg-string-input-${argIndex}`"
/>
@@ -118,7 +118,7 @@
v-model.number="fields.args[argIndex].value"
placeholder="Enter number"
:required="isRequired(fields.functionName, argIndex)"
class="tw-w-52"
class="tw:w-52"
dense
:data-test="`dashboard-function-dropdown-arg-number-input-${argIndex}`"
/>
@@ -140,7 +140,7 @@
fields.args[argIndex].value = newValue.value;
}
"
class="tw-w-52"
class="tw:w-52"
:data-test="`dashboard-function-dropdown-arg-histogram-interval-input-${argIndex}`"
/>
@@ -152,7 +152,7 @@
flat
round
@click="removeArgument(argIndex)"
class="tw-h-10 tw-w-10"
class="tw:h-10 tw:w-10"
:data-test="`dashboard-function-dropdown-arg-remove-button-${argIndex}`"
/>
</div>
@@ -169,7 +169,7 @@
color="primary"
label="+ Add"
padding="5px 14px"
class="tw-mt-3"
class="tw:mt-3"
no-caps
dense
:data-test="`dashboard-function-dropdown-add-argument-button`"
@@ -228,10 +228,10 @@ describe("HTMLRenderer", () => {
wrapper = createWrapper();
const rendererElement = wrapper.find('[data-test="html-renderer"]');
expect(rendererElement.classes()).toContain('tw-prose');
expect(rendererElement.classes()).toContain('tw-prose-sm');
expect(rendererElement.classes()).toContain('tw-max-w-none');
expect(rendererElement.classes()).not.toContain('tw-prose-invert');
expect(rendererElement.classes()).toContain('tw:prose');
expect(rendererElement.classes()).toContain('tw:prose-sm');
expect(rendererElement.classes()).toContain('tw:max-w-none');
expect(rendererElement.classes()).not.toContain('tw:prose-invert');
});
it("should apply dark theme classes when theme is dark", () => {
@@ -248,8 +248,8 @@ describe("HTMLRenderer", () => {
});
const rendererElement = wrapper.find('[data-test="html-renderer"]');
expect(rendererElement.classes()).toContain('tw-prose');
expect(rendererElement.classes()).toContain('tw-prose-invert');
expect(rendererElement.classes()).toContain('tw:prose');
expect(rendererElement.classes()).toContain('tw:prose-invert');
});
it("should toggle theme classes when theme changes", async () => {
@@ -257,7 +257,7 @@ describe("HTMLRenderer", () => {
wrapper = createWrapper();
let rendererElement = wrapper.find('[data-test="html-renderer"]');
expect(rendererElement.classes()).not.toContain('tw-prose-invert');
expect(rendererElement.classes()).not.toContain('tw:prose-invert');
// Unmount and recreate with dark theme to test the toggle effect
wrapper.unmount();
@@ -274,7 +274,7 @@ describe("HTMLRenderer", () => {
});
rendererElement = wrapper.find('[data-test="html-renderer"]');
expect(rendererElement.classes()).toContain('tw-prose-invert');
expect(rendererElement.classes()).toContain('tw:prose-invert');
});
it("should maintain theme classes with content updates", async () => {
@@ -288,7 +288,7 @@ describe("HTMLRenderer", () => {
await wrapper.setProps({ htmlContent: "<p>Updated content</p>" });
const rendererElement = wrapper.find('[data-test="html-renderer"]');
expect(rendererElement.classes()).toContain('tw-prose-invert');
expect(rendererElement.classes()).toContain('tw:prose-invert');
expect(rendererElement.html()).toContain("Updated content");
});
});
@@ -344,9 +344,9 @@ describe("HTMLRenderer", () => {
wrapper = createWrapper();
const rendererElement = wrapper.find('[data-test="html-renderer"]');
expect(rendererElement.classes()).toContain('tw-prose');
expect(rendererElement.classes()).toContain('tw-prose-sm');
expect(rendererElement.classes()).toContain('tw-max-w-none');
expect(rendererElement.classes()).toContain('tw:prose');
expect(rendererElement.classes()).toContain('tw:prose-sm');
expect(rendererElement.classes()).toContain('tw:max-w-none');
});
it("should handle overflow content properly", () => {
@@ -18,8 +18,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<div class="scroll" style="width: 100%; height: 100%; overflow: auto">
<div
:class="[
'tw-prose tw-prose-sm tw-max-w-none',
store.state?.theme === 'dark' && 'tw-prose-invert',
'tw:prose tw:prose-sm tw:max-w-none',
store.state?.theme === 'dark' && 'tw:prose-invert',
]"
v-html="DOMPurify.sanitize(processedContent)"
data-test="html-renderer"
@@ -270,10 +270,10 @@ describe("MarkdownRenderer", () => {
wrapper = createWrapper();
const rendererElement = wrapper.find('[data-test="markdown-renderer"]');
expect(rendererElement.classes()).toContain('tw-prose');
expect(rendererElement.classes()).toContain('tw-prose-sm');
expect(rendererElement.classes()).toContain('tw-max-w-none');
expect(rendererElement.classes()).not.toContain('tw-prose-invert');
expect(rendererElement.classes()).toContain('tw:prose');
expect(rendererElement.classes()).toContain('tw:prose-sm');
expect(rendererElement.classes()).toContain('tw:max-w-none');
expect(rendererElement.classes()).not.toContain('tw:prose-invert');
});
it("should apply dark theme classes when theme is dark", () => {
@@ -290,8 +290,8 @@ describe("MarkdownRenderer", () => {
});
const rendererElement = wrapper.find('[data-test="markdown-renderer"]');
expect(rendererElement.classes()).toContain('tw-prose');
expect(rendererElement.classes()).toContain('tw-prose-invert');
expect(rendererElement.classes()).toContain('tw:prose');
expect(rendererElement.classes()).toContain('tw:prose-invert');
});
it("should toggle theme classes when theme changes", async () => {
@@ -299,7 +299,7 @@ describe("MarkdownRenderer", () => {
wrapper = createWrapper();
let rendererElement = wrapper.find('[data-test="markdown-renderer"]');
expect(rendererElement.classes()).not.toContain('tw-prose-invert');
expect(rendererElement.classes()).not.toContain('tw:prose-invert');
// Unmount and recreate with dark theme to test the toggle effect
wrapper.unmount();
@@ -316,7 +316,7 @@ describe("MarkdownRenderer", () => {
});
rendererElement = wrapper.find('[data-test="markdown-renderer"]');
expect(rendererElement.classes()).toContain('tw-prose-invert');
expect(rendererElement.classes()).toContain('tw:prose-invert');
});
it("should maintain theme classes with content updates", async () => {
@@ -337,7 +337,7 @@ describe("MarkdownRenderer", () => {
await wrapper.setProps({ markdownContent: "# Updated content" });
const rendererElement = wrapper.find('[data-test="markdown-renderer"]');
expect(rendererElement.classes()).toContain('tw-prose-invert');
expect(rendererElement.classes()).toContain('tw:prose-invert');
expect(rendererElement.html()).toContain("Updated content");
});
});
@@ -433,9 +433,9 @@ describe("MarkdownRenderer", () => {
wrapper = createWrapper();
const rendererElement = wrapper.find('[data-test="markdown-renderer"]');
expect(rendererElement.classes()).toContain('tw-prose');
expect(rendererElement.classes()).toContain('tw-prose-sm');
expect(rendererElement.classes()).toContain('tw-max-w-none');
expect(rendererElement.classes()).toContain('tw:prose');
expect(rendererElement.classes()).toContain('tw:prose-sm');
expect(rendererElement.classes()).toContain('tw:max-w-none');
});
it("should handle overflow content properly", () => {
@@ -21,8 +21,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
>
<div
:class="[
'tw-prose tw-prose-sm tw-max-w-none',
store.state?.theme === 'dark' && 'tw-prose-invert',
'tw:prose tw:prose-sm tw:max-w-none',
store.state?.theme === 'dark' && 'tw:prose-invert',
]"
v-html="DOMPurify.sanitize(marked(processedContent))"
data-test="markdown-renderer"

Some files were not shown because too many files have changed in this diff Show More