From e2d15657497489712ca30df0656a9a9f024e4d1e Mon Sep 17 00:00:00 2001 From: Guilhem Date: Thu, 5 Dec 2024 10:07:11 +0000 Subject: [PATCH] polish critical alert modal (#4802) * polish critical alert modal * Use Table component * use table component pagination * Add acknowledge to the table * Polishing table * fix nit * Filter alerts * Add notification count on modal * Adjust table height * Change mute description * Fix layout * minor fix * Fix small screen issue * fix toast on refresh * Revert "fix toast on refresh" This reverts commit ae3593e1af0a0019a7f8a8be724e646d7c514dd1. * filtering to backend, superadmin also acknowledges workspace (unless CLOUD_HOSTED), simplifications * sqlx prep * improve reactivity * improvements --------- Co-authored-by: Alexander Petric Co-authored-by: Alexander Petric Co-authored-by: Ruben Fiszel --- ...113eb91d65b98927eaaba427cbde27dd79bfe.json | 22 ++ ...6eaca91a3616c9bfbd78a408b7079ca9706d4.json | 22 ++ ...c068b72294cc99ff44ed0a13179df508ebc6a.json | 15 - ...36caae3fa256fc0803749ec5107632669adb3.json | 14 - ...ba523c9f17d106298d8781885134d28553d36.json | 15 + ...3d0193a0ee707e6482a892db58915e0c8a27f.json | 20 ++ ...4361e75257c0e5faf8795274ec86236a27413.json | 23 ++ ...976da6c6e331d5748bf6f8d59f9f75c027fe8.json | 16 + backend/windmill-api/openapi.yaml | 34 +- backend/windmill-api/src/settings.rs | 2 +- backend/windmill-api/src/utils.rs | 78 ++++- backend/windmill-api/src/workspaces.rs | 2 +- frontend/src/lib/components/Toggle.svelte | 2 +- .../common/alert/Notification.svelte | 16 + .../common/button/RefreshButton.svelte | 34 ++ .../lib/components/common/layout/List.svelte | 38 +++ .../common/layout/ListElement.svelte | 3 + .../lib/components/common/modal/Modal.svelte | 10 +- .../lib/components/common/modal/Modal2.svelte | 110 +++++++ .../lib/components/common/popup/Popup.svelte | 3 + .../sidebar/CriticalAlertModal.svelte | 178 ++++++++++- .../sidebar/CriticalAlertModalInner.svelte | 296 +++++------------- .../sidebar/CriticalAlertTable.svelte | 146 +++++++++ .../sidebar/SideBarNotification.svelte | 8 +- frontend/src/lib/components/table/Cell.svelte | 8 +- .../src/lib/components/table/DataTable.svelte | 124 ++++---- frontend/src/lib/components/table/Head.svelte | 2 +- frontend/src/lib/components/table/Row.svelte | 4 +- 28 files changed, 899 insertions(+), 346 deletions(-) create mode 100644 backend/.sqlx/query-0a46f1f3047d15227f82ae24ad2113eb91d65b98927eaaba427cbde27dd79bfe.json create mode 100644 backend/.sqlx/query-372eb162ace15a4f07162bb46706eaca91a3616c9bfbd78a408b7079ca9706d4.json delete mode 100644 backend/.sqlx/query-65da41c7ded54cdee8d33211561c068b72294cc99ff44ed0a13179df508ebc6a.json delete mode 100644 backend/.sqlx/query-7c32176755c6ea2b6ae531860d436caae3fa256fc0803749ec5107632669adb3.json create mode 100644 backend/.sqlx/query-80864158f61adaad8df934acc54ba523c9f17d106298d8781885134d28553d36.json create mode 100644 backend/.sqlx/query-951c053cf756f00e1da26b06edb3d0193a0ee707e6482a892db58915e0c8a27f.json create mode 100644 backend/.sqlx/query-b9ed42d4b795942251baafd016b4361e75257c0e5faf8795274ec86236a27413.json create mode 100644 backend/.sqlx/query-cd4067e68b375461a495f402d05976da6c6e331d5748bf6f8d59f9f75c027fe8.json create mode 100644 frontend/src/lib/components/common/alert/Notification.svelte create mode 100644 frontend/src/lib/components/common/button/RefreshButton.svelte create mode 100644 frontend/src/lib/components/common/layout/List.svelte create mode 100644 frontend/src/lib/components/common/layout/ListElement.svelte create mode 100644 frontend/src/lib/components/common/modal/Modal2.svelte create mode 100644 frontend/src/lib/components/sidebar/CriticalAlertTable.svelte diff --git a/backend/.sqlx/query-0a46f1f3047d15227f82ae24ad2113eb91d65b98927eaaba427cbde27dd79bfe.json b/backend/.sqlx/query-0a46f1f3047d15227f82ae24ad2113eb91d65b98927eaaba427cbde27dd79bfe.json new file mode 100644 index 0000000000..5bb7767b0e --- /dev/null +++ b/backend/.sqlx/query-0a46f1f3047d15227f82ae24ad2113eb91d65b98927eaaba427cbde27dd79bfe.json @@ -0,0 +1,22 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT COUNT(*)\n FROM alerts\n WHERE COALESCE(acknowledged, false) = $1", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "count", + "type_info": "Int8" + } + ], + "parameters": { + "Left": [ + "Bool" + ] + }, + "nullable": [ + null + ] + }, + "hash": "0a46f1f3047d15227f82ae24ad2113eb91d65b98927eaaba427cbde27dd79bfe" +} diff --git a/backend/.sqlx/query-372eb162ace15a4f07162bb46706eaca91a3616c9bfbd78a408b7079ca9706d4.json b/backend/.sqlx/query-372eb162ace15a4f07162bb46706eaca91a3616c9bfbd78a408b7079ca9706d4.json new file mode 100644 index 0000000000..68fe6dba85 --- /dev/null +++ b/backend/.sqlx/query-372eb162ace15a4f07162bb46706eaca91a3616c9bfbd78a408b7079ca9706d4.json @@ -0,0 +1,22 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT COUNT(*)\n FROM alerts\n WHERE workspace_id = $1", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "count", + "type_info": "Int8" + } + ], + "parameters": { + "Left": [ + "Text" + ] + }, + "nullable": [ + null + ] + }, + "hash": "372eb162ace15a4f07162bb46706eaca91a3616c9bfbd78a408b7079ca9706d4" +} diff --git a/backend/.sqlx/query-65da41c7ded54cdee8d33211561c068b72294cc99ff44ed0a13179df508ebc6a.json b/backend/.sqlx/query-65da41c7ded54cdee8d33211561c068b72294cc99ff44ed0a13179df508ebc6a.json deleted file mode 100644 index e183d3b95c..0000000000 --- a/backend/.sqlx/query-65da41c7ded54cdee8d33211561c068b72294cc99ff44ed0a13179df508ebc6a.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "UPDATE alerts\n SET\n acknowledged = true,\n acknowledged_workspace = CASE\n WHEN $2::text IS NOT NULL AND workspace_id = $2 THEN true\n ELSE acknowledged_workspace\n END\n WHERE id = $1", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Int4", - "Text" - ] - }, - "nullable": [] - }, - "hash": "65da41c7ded54cdee8d33211561c068b72294cc99ff44ed0a13179df508ebc6a" -} diff --git a/backend/.sqlx/query-7c32176755c6ea2b6ae531860d436caae3fa256fc0803749ec5107632669adb3.json b/backend/.sqlx/query-7c32176755c6ea2b6ae531860d436caae3fa256fc0803749ec5107632669adb3.json deleted file mode 100644 index cf976de33f..0000000000 --- a/backend/.sqlx/query-7c32176755c6ea2b6ae531860d436caae3fa256fc0803749ec5107632669adb3.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "UPDATE alerts \n SET\n acknowledged = true,\n acknowledged_workspace = CASE\n WHEN $1::text IS NOT NULL THEN true\n ELSE acknowledged_workspace\n END\n WHERE ($1::text IS NOT NULL AND workspace_id = $1)\n OR ($1::text IS NULL)", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Text" - ] - }, - "nullable": [] - }, - "hash": "7c32176755c6ea2b6ae531860d436caae3fa256fc0803749ec5107632669adb3" -} diff --git a/backend/.sqlx/query-80864158f61adaad8df934acc54ba523c9f17d106298d8781885134d28553d36.json b/backend/.sqlx/query-80864158f61adaad8df934acc54ba523c9f17d106298d8781885134d28553d36.json new file mode 100644 index 0000000000..5be45ac0fb --- /dev/null +++ b/backend/.sqlx/query-80864158f61adaad8df934acc54ba523c9f17d106298d8781885134d28553d36.json @@ -0,0 +1,15 @@ +{ + "db_name": "PostgreSQL", + "query": "UPDATE alerts \n SET\n acknowledged = true,\n acknowledged_workspace = CASE\n WHEN $2 THEN\n CASE\n WHEN $1::text IS NOT NULL THEN true\n ELSE acknowledged_workspace\n END\n ELSE true\n END\n WHERE ($1::text IS NOT NULL AND workspace_id = $1)\n OR ($1::text IS NULL)", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Text", + "Bool" + ] + }, + "nullable": [] + }, + "hash": "80864158f61adaad8df934acc54ba523c9f17d106298d8781885134d28553d36" +} diff --git a/backend/.sqlx/query-951c053cf756f00e1da26b06edb3d0193a0ee707e6482a892db58915e0c8a27f.json b/backend/.sqlx/query-951c053cf756f00e1da26b06edb3d0193a0ee707e6482a892db58915e0c8a27f.json new file mode 100644 index 0000000000..7c51668501 --- /dev/null +++ b/backend/.sqlx/query-951c053cf756f00e1da26b06edb3d0193a0ee707e6482a892db58915e0c8a27f.json @@ -0,0 +1,20 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT COUNT(*)\n FROM alerts", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "count", + "type_info": "Int8" + } + ], + "parameters": { + "Left": [] + }, + "nullable": [ + null + ] + }, + "hash": "951c053cf756f00e1da26b06edb3d0193a0ee707e6482a892db58915e0c8a27f" +} diff --git a/backend/.sqlx/query-b9ed42d4b795942251baafd016b4361e75257c0e5faf8795274ec86236a27413.json b/backend/.sqlx/query-b9ed42d4b795942251baafd016b4361e75257c0e5faf8795274ec86236a27413.json new file mode 100644 index 0000000000..40f0418eef --- /dev/null +++ b/backend/.sqlx/query-b9ed42d4b795942251baafd016b4361e75257c0e5faf8795274ec86236a27413.json @@ -0,0 +1,23 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT COUNT(*)\n FROM alerts\n WHERE workspace_id = $1 AND COALESCE(acknowledged_workspace, false) = $2", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "count", + "type_info": "Int8" + } + ], + "parameters": { + "Left": [ + "Text", + "Bool" + ] + }, + "nullable": [ + null + ] + }, + "hash": "b9ed42d4b795942251baafd016b4361e75257c0e5faf8795274ec86236a27413" +} diff --git a/backend/.sqlx/query-cd4067e68b375461a495f402d05976da6c6e331d5748bf6f8d59f9f75c027fe8.json b/backend/.sqlx/query-cd4067e68b375461a495f402d05976da6c6e331d5748bf6f8d59f9f75c027fe8.json new file mode 100644 index 0000000000..1d1b577f7a --- /dev/null +++ b/backend/.sqlx/query-cd4067e68b375461a495f402d05976da6c6e331d5748bf6f8d59f9f75c027fe8.json @@ -0,0 +1,16 @@ +{ + "db_name": "PostgreSQL", + "query": "UPDATE alerts\n SET\n acknowledged = true,\n acknowledged_workspace = CASE\n WHEN $3 THEN\n CASE\n WHEN $2::text IS NOT NULL AND workspace_id = $2 THEN true\n ELSE acknowledged_workspace\n END\n ELSE true\n END\n WHERE id = $1", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Int4", + "Text", + "Bool" + ] + }, + "nullable": [] + }, + "hash": "cd4067e68b375461a495f402d05976da6c6e331d5748bf6f8d59f9f75c027fe8" +} diff --git a/backend/windmill-api/openapi.yaml b/backend/windmill-api/openapi.yaml index f0bd8856d5..4ec45bbdfa 100644 --- a/backend/windmill-api/openapi.yaml +++ b/backend/windmill-api/openapi.yaml @@ -872,9 +872,20 @@ paths: content: application/json: schema: - type: array - items: - $ref: '#/components/schemas/CriticalAlert' + type: object + properties: + alerts: + type: array + items: + $ref: '#/components/schemas/CriticalAlert' + total_rows: + type: integer + description: Total number of rows matching the query. + example: 100 + total_pages: + type: integer + description: Total number of pages based on the page size. + example: 10 /settings/critical_alerts/{id}/acknowledge: post: @@ -2757,9 +2768,20 @@ paths: content: application/json: schema: - type: array - items: - $ref: '#/components/schemas/CriticalAlert' + type: object + properties: + alerts: + type: array + items: + $ref: '#/components/schemas/CriticalAlert' + total_rows: + type: integer + description: Total number of rows matching the query. + example: 100 + total_pages: + type: integer + description: Total number of pages based on the page size. + example: 10 /w/{workspace}/workspaces/critical_alerts/{id}/acknowledge: post: diff --git a/backend/windmill-api/src/settings.rs b/backend/windmill-api/src/settings.rs index 61a7e46fef..c527eb481a 100644 --- a/backend/windmill-api/src/settings.rs +++ b/backend/windmill-api/src/settings.rs @@ -448,7 +448,7 @@ pub async fn get_critical_alerts( Extension(db): Extension, authed: ApiAuthed, Query(params): Query, -) -> JsonResult> { +) -> JsonResult { require_devops_role(&db, &authed.email).await?; crate::utils::get_critical_alerts(db, params, None).await diff --git a/backend/windmill-api/src/utils.rs b/backend/windmill-api/src/utils.rs index cc6360b994..a003481c9a 100644 --- a/backend/windmill-api/src/utils.rs +++ b/backend/windmill-api/src/utils.rs @@ -10,6 +10,7 @@ use axum::{body::Body, response::Response}; use regex::Regex; use serde::Deserialize; use sqlx::{Postgres, Transaction}; +use windmill_common::worker::CLOUD_HOSTED; use windmill_common::{ auth::{is_devops_email, is_super_admin_email}, error::{self, Error}, @@ -206,11 +207,55 @@ pub async fn get_critical_alerts( db: DB, params: AlertQueryParams, workspace_id: Option, -) -> JsonResult> { +) -> JsonResult { + // Returning total rows and total pages let page = params.page.unwrap_or(1).max(1); let page_size = params.page_size.unwrap_or(10).min(100) as i64; let offset = ((page - 1) * page_size as i32) as i64; + // Count total rows + let total_rows = if let Some(workspace_id) = &workspace_id { + if params.acknowledged.is_none() { + sqlx::query_scalar!( + "SELECT COUNT(*) + FROM alerts + WHERE workspace_id = $1", + workspace_id + ) + .fetch_one(&db) + .await? + } else { + sqlx::query_scalar!( + "SELECT COUNT(*) + FROM alerts + WHERE workspace_id = $1 AND COALESCE(acknowledged_workspace, false) = $2", + workspace_id, + params.acknowledged + ) + .fetch_one(&db) + .await? + } + } else { + if params.acknowledged.is_none() { + sqlx::query_scalar!( + "SELECT COUNT(*) + FROM alerts" + ) + .fetch_one(&db) + .await? + } else { + sqlx::query_scalar!( + "SELECT COUNT(*) + FROM alerts + WHERE COALESCE(acknowledged, false) = $1", + params.acknowledged + ) + .fetch_one(&db) + .await? + } + }; + + // Fetch paginated rows let alerts = if let Some(workspace_id) = workspace_id { // `workspace_id` is provided => workspace admin if params.acknowledged.is_none() { @@ -278,7 +323,14 @@ pub async fn get_critical_alerts( } }; - Ok(Json(alerts)) + let total_rows = total_rows.unwrap_or(0); + let total_pages = ((total_rows as f64) / (page_size as f64)).ceil() as i64; + + Ok(Json(serde_json::json!({ + "alerts": alerts, + "total_rows": total_rows, + "total_pages": total_pages + }))) } #[cfg(feature = "enterprise")] @@ -292,12 +344,17 @@ pub async fn acknowledge_critical_alert( SET acknowledged = true, acknowledged_workspace = CASE - WHEN $2::text IS NOT NULL AND workspace_id = $2 THEN true - ELSE acknowledged_workspace + WHEN $3 THEN + CASE + WHEN $2::text IS NOT NULL AND workspace_id = $2 THEN true + ELSE acknowledged_workspace + END + ELSE true END WHERE id = $1", id, - workspace_id + workspace_id, + *CLOUD_HOSTED ) .execute(&db) .await?; @@ -320,12 +377,17 @@ pub async fn acknowledge_all_critical_alerts( SET acknowledged = true, acknowledged_workspace = CASE - WHEN $1::text IS NOT NULL THEN true - ELSE acknowledged_workspace + WHEN $2 THEN + CASE + WHEN $1::text IS NOT NULL THEN true + ELSE acknowledged_workspace + END + ELSE true END WHERE ($1::text IS NOT NULL AND workspace_id = $1) OR ($1::text IS NULL)", - workspace_id + workspace_id, + *CLOUD_HOSTED ) .execute(&db) .await?; diff --git a/backend/windmill-api/src/workspaces.rs b/backend/windmill-api/src/workspaces.rs index c6d6e5301c..bba06b238b 100644 --- a/backend/windmill-api/src/workspaces.rs +++ b/backend/windmill-api/src/workspaces.rs @@ -3111,7 +3111,7 @@ pub async fn get_critical_alerts( Path(w_id): Path, authed: ApiAuthed, Query(params): Query, -) -> JsonResult> { +) -> JsonResult { require_admin_or_devops(authed.is_admin, &authed.username, &authed.email, &db).await?; crate::utils::get_critical_alerts(db, params, Some(w_id)).await diff --git a/frontend/src/lib/components/Toggle.svelte b/frontend/src/lib/components/Toggle.svelte index c50b29414a..73638bce9c 100644 --- a/frontend/src/lib/components/Toggle.svelte +++ b/frontend/src/lib/components/Toggle.svelte @@ -31,7 +31,7 @@