mirror of
https://git.mirrors.martin98.com/https://github.com/SigNoz/signoz
synced 2025-08-12 20:19:13 +08:00
feat: dashboard revamp according to the latest designs (#4868)
* feat: dashboard list view * feat: update sort menu items * feat: wire up add / import dashboard functionss * feat: update import json styles * feat: new dashboard templates modal * feat: add template filter logic * feat: revamp the overview settings modal (#4894) * feat: revamp the overview settings modal * feat: dashboard settings variable landing page * feat: dashboard add variable button settings * feat: add variable modal changes * feat: handle the unsaved changes for general settings * feat: follow ups for side panel section for dashboards (#4906) * feat: changes for tags input * feat: side panel header styles * feat: changes for textbox variable * feat: handle changes for custom type variable * feat: overflow preview vales * feat: overflow preview vales * feat: setup for new dashboard landing page (#4921) * feat: setup for new dashboard landing page * feat: added empty state widgets * feat: added functionality to the configure and the add panel button * feat: tag variables changes * feat: dashboard revamp changes follow ups (#4929) * feat: changes for new panel type modal * fix: added missing / in the breadcrumbs * feat: added dashboard settings menu items * feat: added dashboard rename modal * feat: move full screen handle a few components up * feat: handle rename and copy export changes * feat: minor cleanup * feat: delete button changes * feat: dashboard widget edit page design revamp (#4946) * feat: dashboard edit page base setup * feat: right container design revamp * feat: alerts and thresholds changes right container * feat: right container * feat: fix graph styles * fix: some edits for dashboard edit page * feat: threshold preview changes (#4990) * feat: threshold preview changes * feat: threshold preview changes * feat: threshold discard handler * fix: remove the horizontal scroll from the dashboards landing page * fix: added margin to dashboard widgets (#4991) * fix: rebase conflicts * feat: dashboard panel grouping change for new designs (#4992) * feat: dashboard panel groping base cleanups * feat: move add panel code from inner component to parent component * feat: new dashboard section panel naming modal * feat: dashboard panel groping base cleanups * feat: grip changes * feat: dashboard list page revamp and functionality additions (#4994) * feat: fix types and code structure for list page * feat: dashboard actions * feat: design changes for tags * feat: design changes for tags * feat: update import json styles * feat: added all dashboards row * feat: added configure metadata linking * feat: added local storage changes for dynamic columns * feat: added user metadata display for metadata modal * feat: configure metadata final changes * feat: handle dashboard list loading state * feat: sort and pagination changes for dashboard list view designs (#4996) * feat: minor list view css changes * feat: added sort functionality to the dashboards list * feat: added sort functionality to the dashboards list * feat: added name dropdown in the settings drawer and image as base64 format (#5000) * feat: added name input in settings drawer * feat: discard handler * feat: implemented the name dropdown * feat: added dashboard list page header * fix: margin of dashboard list container * feat: dashboard empty state (#5005) * feat: light mode changes for new dashboard revamp (#5006) * feat: light mode changes for dahsboards list page * feat: dashboard description landing page changes * feat: variable panels landing page light theme changes * feat: dashboard edit panel light mode * feat: added dashboard list error state (#5011) * feat: added missing light mode designs * fix: usability / customer issues (#5014) * fix: [GH-4986]: preview values not getting updated when the query result is empty * fix: [GH-4985]: fix the usability of dahsboards variables drawer * fix: light mode design for component slider * fix: code cleanup * fix: 0 being added in case of no tags * fix: minor styling fixes * fix: handle silent error for dashboard edit mutation (#5022) * fix: handle silent error for dashboard edit mutation * fix: handle silent error for dashboard edit mutation * fix: rbac changes * fix: grip icon color * fix: new dashboards feedback from testing (#5030) * fix: hide create new dashboards from viewer roles * fix: move the elipsis button right of date time picker and make it a button * fix: remove duplicate button from actions for now * fix: last updated by and created by difference * fix: hide intercom for modals * fix: actions popover not closing * fix: temp remove templates modal from normal flow * fix: delete button event propagation * fix: minor UI fixes (#5032) * fix: update dashboards landing page icons * fix: added lock icon for locked dashboards * fix: updated dashboards list page styles * fix: comment out filters code for phase 2 (#5034) * fix: dashboard revamp ui fixes (#5037) * fix: increase the width of the graph section * fix: remove select and next from component slider * Dashboard vqa 1 (#5090) * fix: dashboard VQA pt 1 * fix: dashboard VQA pt 2 * fix: dashboard VQA pt 3 * fix: dashboard VQA pt 4 * fix: dashboard VQA pt 5 * fix: dashboard VQA pt 6 * fix: dashboard VQA pt 7 * fix: added dashboard locked footer and base64 icons (#5091) * fix: added dashboard locked footer * fix: update base64 images * fix: dashboard delete hover and row actions refactor * fix: dashboard vqa pt 2 (#5098) * fix: review comments * fix: alerts plot tag spacing * fix: css variables update --------- Co-authored-by: Vikrant Gupta <vikrant.thomso@gmail.com>
This commit is contained in:
parent
3538815331
commit
932d892d9e
@ -85,7 +85,7 @@
|
||||
"less": "^4.1.2",
|
||||
"less-loader": "^10.2.0",
|
||||
"lodash-es": "^4.17.21",
|
||||
"lucide-react": "0.321.0",
|
||||
"lucide-react": "0.379.0",
|
||||
"mini-css-extract-plugin": "2.4.5",
|
||||
"papaparse": "5.4.1",
|
||||
"react": "18.2.0",
|
||||
|
1
frontend/public/Icons/dashboard_emoji.svg
Normal file
1
frontend/public/Icons/dashboard_emoji.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg width="32" height="32" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M13.72 12.839l-9.054.92s.05.649.236.798c.178.142 5.617.066 11.048.088 5.433.023 10.82.125 10.944.072.249-.107.249-.992.249-.992l-13.424-.886zM16.55 7.787l-12.623-.32s.275.61.637.813c.523.29 3.71.889 11.518.918 7.808.028 10.635-.4 11.317-.678.58-.238 1.215-1.576 1.215-1.576l-12.064.843z" fill="#8A1E0C"/><path d="M21.95 8.658v1.335l2.176-.087V8.542l-2.176.116z" fill="#8A1E0C"/><path d="M21.948 9.566h2.177v16.797l-2.206.294.029-17.09z" fill="#EB2901"/><path d="M21.355 26.19c-.111.193-.111 2.297-.007 2.444.105.147 3.242.104 3.326 0 .085-.104.063-2.38 0-2.464-.062-.085-3.235-.125-3.32.02z" fill="#474C4F"/><path d="M8.462 9.85V8.488l2.042.125v1.22l-2.042.017z" fill="#8A1E0C"/><path d="M8.462 9.55l-.038 17.051 2.08-.207V9.566l-2.042-.015z" fill="#EB2901"/><path d="M7.804 25.919c-.073.073-.147 2.36-.02 2.464.125.104 3.14.129 3.244.024.105-.104.085-2.304.023-2.43-.063-.127-3.142-.163-3.247-.058z" fill="#474C4F"/><path d="M14.788 8.107v4.876l2.393-.33V8.108h-2.393z" fill="#EB2901"/><path d="M27.067 11.978c-.115-.16-.482-.138-.482-.138l-1.137-.013c.002-.398-.01-.913-.078-.996-.116-.137-4.542-.09-4.702.047-.091.078-.11.527-.107.898-2.738-.027-5.99-.058-8.83-.076 0-.384-.012-.849-.078-.915-.116-.116-4.22-.185-4.38-.07-.113.083-.136.647-.138.97-1.384.002-2.275.013-2.34.04-.322.137-.137 2.042-.137 2.042l22.476.16c.002.002.049-1.787-.067-1.95z" fill="#EB2901"/><path d="M3.93 6.942s-.646-.34-1.377-1.573c-.509-.858-.595-1.658-.387-1.778.21-.12 2.154 1.08 5.745 1.616a60.81 60.81 0 008.173.644c2.884.027 5.717-.135 8.397-.644 3.62-.689 4.906-1.436 5.264-1.316.36.12-.109 1.227-.369 1.78-.178.376-.944 1.77-1.515 1.87-.411.072-19.953-.09-19.953-.09l-3.977-.509z" fill="#474C4F"/><path d="M3.31 5.724c-.108.137-.057.457.212 1.06.107.237.415.782.529.917 0 0 2.982.756 11.977.7 8.995-.055 12.108-.62 12.108-.62s.911-1.277.745-1.32c-.096-.024-4.847.98-12.909.898C7.911 7.277 3.311 5.724 3.311 5.724z" fill="#EB2901"/></svg>
|
After Width: | Height: | Size: 2.0 KiB |
1
frontend/public/Icons/dashboards.svg
Normal file
1
frontend/public/Icons/dashboards.svg
Normal file
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 5.2 KiB |
1
frontend/public/Icons/landscape.svg
Normal file
1
frontend/public/Icons/landscape.svg
Normal file
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 6.1 KiB |
1
frontend/public/Icons/tools.svg
Normal file
1
frontend/public/Icons/tools.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg width="14" height="14" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M1.305 13.063c.74.739 1.637.482 2.156-.109.53-.604.813-.956.813-.956.66-.973 3.392-4.227 5.724-6.568a2.638 2.638 0 002.74-.434 2.648 2.648 0 00.922-2.041.155.155 0 00-.23-.132l-1.607.927a1.64 1.64 0 01-1.076-1.864l1.6-.923a.153.153 0 00.077-.134.153.153 0 00-.077-.133 2.65 2.65 0 00-3.66 3.563C6.11 6.826 2.966 9.604 2.15 10.223c0 0-.492.356-.962.84-.464.476-.636 1.245.117 1.999zm.542-1.137a.592.592 0 111.184 0 .592.592 0 01-1.184 0z" fill="#82AEC0"/><path d="M8.334 4.61l.353-.35a2.63 2.63 0 01-.212-2.039c.073-.12.189-.249.262-.171-.03.946.245 1.931.902 2.611.327.338.752.582 1.207.696.224.057.458.082.69.069.137-.008.519-.149.596-.044v.004a2.656 2.656 0 01-2.135.043 38.176 38.176 0 00-1.903 2.05c.262-.495 1.034-1.408 1.241-1.757a.412.412 0 00-.036-.464c-.207-.255-.633-.493-.965-.649z" fill="#2F7889"/><path d="M5.186 8.529c.06-.062.004-.167-.08-.148-.158.035-.386.125-.657.345-.531.43-1.934 1.595-2.107 1.825-.173.23.522-.003.767-.047.2-.036 1.602-1.48 2.077-1.975zM10.048 1.104c-.296.212-.563.465-.84.701-.072.061-.177.122-.25.065-.08-.064-.03-.191.03-.274C9.512.874 10.493.358 11.442.563c-.5.161-.95.223-1.395.541z" fill="#B9E4EA"/><path d="M12.408 3.583a2.1 2.1 0 01-.371.19c-.112.031-.43-.092-.522-.166l1.183-.772c.043-.028.087-.056.137-.072a.546.546 0 01.185-.014c.087.004.51-.01.56.064.05.075-.126.149-.183.183-.33.197-.66.391-.99.587zM7.867 7.687L6.624 6.254c-.45.423-.895.835-1.321 1.225l.362-.078a.482.482 0 01.439.13l.58.65c.122.122.142.334.096.5l-.065.308c.367-.423.755-.862 1.152-1.302z" fill="#2F7889"/><g><path d="M13.378 12.86l-.744.643a.686.686 0 01-.968-.072L2.84 2.779l1.135-.853 9.459 9.976a.668.668 0 01-.057.957z" fill="#A06841"/><path d="M3.648 3.752l2.1 2.535c.328-.493.494-1.084.629-1.83l-2.028-2.14a1.838 1.838 0 00-.414.48 2.17 2.17 0 00-.287.955z" fill="#7D5133"/><path d="M7.81.438C5.885.416 5.17.588 4.098 1.515l-.966.835c-.35.302-.815.566-.742 1.089.027.19.086.384.05.573-.034.179-.242.268-.39.166-.139-.096-.292-.214-.463-.234a.588.588 0 00-.45.14l-.747.664s-.107.434.729 1.38c.835.946 1.373.878 1.373.878l.702-.618a.53.53 0 00.176-.412c-.003-.184-.11-.326-.174-.49-.013-.031-.083-.143.04-.244.109-.094.333-.062.46-.027.129.034.25.088.38.122.25.065.369-.051.543-.201L6.013 3.93c.619-.536-.325-1.474-.325-1.474C5.244 1.953 7.941.687 7.941.687c.198-.069.138-.246-.13-.249z" fill="#82AEC0"/><path d="M4.076 5.338a.504.504 0 00.14.016v-.02c-.011-.12-.077-.23-.144-.33A7.18 7.18 0 002.545 3.33a1.683 1.683 0 00-.154-.111.726.726 0 00-.002.22c.027.19.086.384.05.573-.038.196-.242.25-.399.177a3.27 3.27 0 011.011 1.027c.035.056.07.115.11.168a.2.2 0 01.075-.14c.109-.095.333-.063.46-.029.13.034.25.088.38.123zM1.778 5.573c.585.613.914 1.247.734 1.42-.179.17-.799-.186-1.384-.797C.542 5.584.21 4.92.388 4.748c.18-.171.804.213 1.39.825z" fill="#2F7889"/><path d="M4.057 2.41c.465-.198.88-.623 1.422-1.09A2.53 2.53 0 016.03.964c.076-.035.048-.149-.036-.148-.278.005-.527.09-.772.196-.342.149-.644.374-.935.608-.2.16-.67.555-.965.805-.055.047-.012.12.06.12.208.002.325.014.674-.135zM1.124 4.352c-.196.221.055.281.496.646.311.257.642.018.645-.223.003-.216-.052-.333-.366-.53-.315-.199-.597-.093-.775.107z" fill="#B9E4EA"/></g></svg>
|
After Width: | Height: | Size: 3.2 KiB |
234
frontend/public/Images/blankDashboardTemplatePreview.svg
Normal file
234
frontend/public/Images/blankDashboardTemplatePreview.svg
Normal file
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 204 KiB |
9
frontend/public/Images/redisTemplatePreview.svg
Normal file
9
frontend/public/Images/redisTemplatePreview.svg
Normal file
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 1.7 MiB |
@ -1,6 +1,6 @@
|
||||
{
|
||||
"create_dashboard": "Create Dashboard",
|
||||
"import_json": "Import JSON",
|
||||
"import_json": "Import Dashboard JSON",
|
||||
"import_grafana_json": "Import Grafana JSON",
|
||||
"copy_to_clipboard": "Copy To ClipBoard",
|
||||
"download_json": "Download JSON",
|
||||
@ -9,7 +9,7 @@
|
||||
"upload_json_file": "Upload JSON file",
|
||||
"paste_json_below": "Paste JSON below",
|
||||
"error_upload_json": "Invalid JSON",
|
||||
"load_json": "Load JSON",
|
||||
"import_and_next": "Import and Next",
|
||||
"import_dashboard_by_pasting": "Import dashboard by pasting JSON or importing JSON file",
|
||||
"error_loading_json": "Error loading JSON file",
|
||||
"empty_json_not_allowed": "Empty JSON is not allowed",
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"create_dashboard": "Create Dashboard",
|
||||
"import_json": "Import JSON",
|
||||
"import_json": "Import Dashboard JSON",
|
||||
"import_grafana_json": "Import Grafana JSON",
|
||||
"copy_to_clipboard": "Copy To ClipBoard",
|
||||
"download_json": "Download JSON",
|
||||
@ -9,7 +9,7 @@
|
||||
"upload_json_file": "Upload JSON file",
|
||||
"paste_json_below": "Paste JSON below",
|
||||
"error_upload_json": "Invalid JSON",
|
||||
"load_json": "Load JSON",
|
||||
"import_and_next": "Import and Next",
|
||||
"import_dashboard_by_pasting": "Import dashboard by pasting JSON or importing JSON file",
|
||||
"error_loading_json": "Error loading JSON file",
|
||||
"empty_json_not_allowed": "Empty JSON is not allowed",
|
||||
|
@ -1,26 +1,20 @@
|
||||
import axios from 'api';
|
||||
import { ErrorResponseHandler } from 'api/ErrorResponseHandler';
|
||||
import { AxiosError } from 'axios';
|
||||
import { ErrorResponse, SuccessResponse } from 'types/api';
|
||||
import { PayloadProps, Props } from 'types/api/dashboard/update';
|
||||
|
||||
const updateDashboard = async (
|
||||
props: Props,
|
||||
): Promise<SuccessResponse<PayloadProps> | ErrorResponse> => {
|
||||
try {
|
||||
const response = await axios.put(`/dashboards/${props.uuid}`, {
|
||||
...props.data,
|
||||
});
|
||||
const response = await axios.put(`/dashboards/${props.uuid}`, {
|
||||
...props.data,
|
||||
});
|
||||
|
||||
return {
|
||||
statusCode: 200,
|
||||
error: null,
|
||||
message: response.data.status,
|
||||
payload: response.data.data,
|
||||
};
|
||||
} catch (error) {
|
||||
return ErrorResponseHandler(error as AxiosError);
|
||||
}
|
||||
return {
|
||||
statusCode: 200,
|
||||
error: null,
|
||||
message: response.data.status,
|
||||
payload: response.data.data,
|
||||
};
|
||||
};
|
||||
|
||||
export default updateDashboard;
|
||||
|
176
frontend/src/assets/CustomIcons/ApacheIcon.tsx
Normal file
176
frontend/src/assets/CustomIcons/ApacheIcon.tsx
Normal file
@ -0,0 +1,176 @@
|
||||
export default function ApacheIcon(): JSX.Element {
|
||||
return (
|
||||
<svg
|
||||
width="14"
|
||||
height="14"
|
||||
viewBox="0 0 14 14"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M8.7112 1.05739C8.3796 1.30831 8.08524 1.60498 7.83691 1.93853L8.17977 2.58567C8.40218 2.26279 8.64677 1.95576 8.91177 1.66681C8.93063 1.64581 8.94091 1.63596 8.94091 1.63596L8.91177 1.66681C8.66003 1.95965 8.43081 2.27111 8.22606 2.59853C8.67305 2.56672 9.11808 2.51165 9.55934 2.43353C9.60936 2.25525 9.62374 2.06886 9.60168 1.88502C9.57962 1.70118 9.52154 1.52349 9.43077 1.3621C9.43077 1.3621 9.09991 0.828957 8.7112 1.05739Z"
|
||||
fill="url(#paint0_linear_2061_4195)"
|
||||
/>
|
||||
<path
|
||||
d="M7.55932 6.49365L7.42432 6.51722L7.49332 6.50651C7.51432 6.50265 7.53703 6.49837 7.55932 6.49365Z"
|
||||
fill="#BE202E"
|
||||
/>
|
||||
<path
|
||||
opacity="0.35"
|
||||
d="M7.55932 6.49365L7.42432 6.51722L7.49332 6.50651C7.51432 6.50265 7.53703 6.49837 7.55932 6.49365Z"
|
||||
fill="#BE202E"
|
||||
/>
|
||||
<path
|
||||
d="M7.67407 5.92858L7.6955 5.92558C7.72464 5.9213 7.75336 5.91616 7.78122 5.91016L7.67493 5.92816L7.67407 5.92858Z"
|
||||
fill="#BE202E"
|
||||
/>
|
||||
<path
|
||||
opacity="0.35"
|
||||
d="M7.67407 5.92858L7.6955 5.92558C7.72464 5.9213 7.75336 5.91616 7.78122 5.91016L7.67493 5.92816L7.67407 5.92858Z"
|
||||
fill="#BE202E"
|
||||
/>
|
||||
<path
|
||||
d="M7.16872 4.25736C7.273 4.0625 7.37858 3.87221 7.48543 3.6865C7.59629 3.49393 7.70829 3.3075 7.82143 3.12721L7.84115 3.09507C7.95315 2.91793 8.066 2.74779 8.17972 2.58464L7.83686 1.9375L7.75886 2.03393C7.65986 2.15736 7.55743 2.29107 7.452 2.43036C7.33329 2.58893 7.21115 2.75779 7.08729 2.93564C6.97286 3.09979 6.85672 3.27164 6.74058 3.44993C6.64158 3.60121 6.54258 3.75721 6.444 3.91707L6.43286 3.93507L6.879 4.8145C6.97443 4.62707 7.071 4.44136 7.16872 4.25736Z"
|
||||
fill="url(#paint1_linear_2061_4195)"
|
||||
/>
|
||||
<path
|
||||
d="M5.13606 9.22519C5.07692 9.38748 5.01763 9.55305 4.95821 9.72191L4.95563 9.72919L4.93035 9.80076C4.89049 9.91476 4.85535 10.015 4.77563 10.2503C4.92444 10.3467 5.0416 10.4847 5.11249 10.6472C5.10283 10.4581 5.01907 10.2804 4.87935 10.1526C5.16043 10.1981 5.44862 10.1654 5.71236 10.0581C5.97609 9.95074 6.20521 9.77291 6.37463 9.54405C6.40102 9.50088 6.42464 9.45607 6.44535 9.40991C6.37459 9.49741 6.28141 9.56408 6.17575 9.6028C6.07008 9.64152 5.95589 9.65084 5.84535 9.62976C6.20937 9.49539 6.51802 9.24316 6.72221 8.91319C6.76978 8.83691 6.81563 8.75376 6.86278 8.66162C6.6974 8.84328 6.48756 8.97872 6.25393 9.05463C6.02029 9.13053 5.77091 9.14426 5.53035 9.09448L5.16949 9.13391C5.15706 9.16434 5.14721 9.19476 5.13606 9.22519Z"
|
||||
fill="url(#paint2_linear_2061_4195)"
|
||||
/>
|
||||
<path
|
||||
d="M5.30448 8.417C5.38248 8.21528 5.46276 8.01157 5.54533 7.80586C5.62419 7.60871 5.70519 7.41057 5.78833 7.21143C5.87148 7.01228 5.95633 6.81228 6.0429 6.61143C6.1309 6.40828 6.22076 6.20557 6.31248 6.00328C6.40419 5.801 6.49633 5.60257 6.5889 5.408C6.62262 5.33714 6.65662 5.26643 6.6909 5.19586C6.75005 5.07414 6.80962 4.95343 6.86962 4.83371C6.87262 4.82728 6.87605 4.82086 6.87948 4.81443L6.4329 3.93457L6.41105 3.97014C6.30733 4.14157 6.20362 4.313 6.10205 4.49128C6.00048 4.66957 5.89848 4.853 5.80205 5.03814C5.7189 5.19443 5.63776 5.35157 5.55862 5.50957L5.51148 5.606C5.41419 5.80614 5.32633 5.99943 5.24705 6.18543C5.15705 6.396 5.07762 6.59686 5.00876 6.788C4.9629 6.91357 4.92305 7.03486 4.88362 7.151C4.85233 7.25043 4.82276 7.34986 4.79448 7.451C4.72762 7.68471 4.67048 7.91771 4.62305 8.15L5.07133 9.035C5.13076 8.87671 5.19162 8.71614 5.2539 8.55328L5.30448 8.417Z"
|
||||
fill="url(#paint3_linear_2061_4195)"
|
||||
/>
|
||||
<path
|
||||
d="M4.615 8.18082C4.55868 8.46014 4.51975 8.74268 4.49843 9.02682C4.49843 9.03668 4.49843 9.04653 4.49629 9.05639C4.3547 8.8778 4.1801 8.72808 3.982 8.61539C4.24526 8.95089 4.41819 9.34823 4.48429 9.76953C4.28982 9.78674 4.0942 9.75338 3.91643 9.67268C4.05057 9.80984 4.21714 9.91096 4.40072 9.96668C4.14974 10.0146 3.9168 10.1307 3.72743 10.3022C3.97375 10.178 4.25059 10.1271 4.525 10.1557C4.21858 11.024 3.91129 11.9827 3.604 13.0001C3.64664 12.988 3.6856 12.9655 3.71739 12.9346C3.74918 12.9037 3.7728 12.8654 3.78615 12.8231C3.841 12.6388 4.20443 11.4298 4.77443 9.84068L4.82372 9.70439L4.83743 9.66625C4.89772 9.49968 4.96015 9.32968 5.02472 9.15625L5.06758 9.03753V9.03539L4.62143 8.15039C4.61929 8.16025 4.61672 8.17053 4.615 8.18082Z"
|
||||
fill="url(#paint4_linear_2061_4195)"
|
||||
/>
|
||||
<path
|
||||
d="M6.94843 4.89059L6.90986 4.96987C6.87129 5.04959 6.83214 5.13102 6.79243 5.21416C6.74957 5.30416 6.70671 5.3963 6.66386 5.49059C6.64157 5.53816 6.621 5.58573 6.59743 5.63416C6.53057 5.7793 6.46286 5.92916 6.39429 6.08373C6.30857 6.27402 6.22286 6.47173 6.13714 6.67687C6.054 6.87259 5.96971 7.07516 5.88429 7.28459C5.80314 7.48459 5.721 7.68973 5.63786 7.90002C5.56386 8.08888 5.48914 8.28273 5.41371 8.48159C5.40986 8.49145 5.40643 8.50087 5.403 8.51073C5.32814 8.70873 5.25257 8.91187 5.17629 9.12016L5.17114 9.1343L5.532 9.09487L5.51057 9.09102C6.039 8.98351 6.52026 8.7126 6.88629 8.31659C7.0686 8.1182 7.22683 7.89896 7.35771 7.66345C7.47147 7.46033 7.57252 7.25036 7.66029 7.03473C7.74386 6.83245 7.824 6.61387 7.90114 6.37645C7.79448 6.43086 7.68084 6.47037 7.56343 6.49388C7.54157 6.49859 7.52057 6.50287 7.49657 6.50673C7.47257 6.51059 7.45071 6.51445 7.42757 6.51745C7.80325 6.36305 8.10458 6.06924 8.26843 5.69759C8.12155 5.79773 7.95733 5.86966 7.78414 5.90973C7.75586 5.91616 7.72757 5.92087 7.69843 5.92516L7.677 5.92816C7.80484 5.87656 7.92565 5.80903 8.03657 5.72716C8.05843 5.71087 8.07943 5.69373 8.1 5.67573C8.13129 5.64873 8.16086 5.62045 8.18914 5.59002C8.20714 5.57116 8.22471 5.55145 8.24186 5.53087C8.28293 5.48179 8.32059 5.42996 8.35457 5.37573C8.36529 5.35859 8.376 5.34145 8.38629 5.32345C8.39957 5.29773 8.41243 5.27245 8.42486 5.24716C8.481 5.13402 8.526 5.03288 8.56157 4.94459C8.57957 4.90173 8.595 4.85888 8.60829 4.82116C8.61386 4.80616 8.619 4.79116 8.62371 4.7783C8.63786 4.73545 8.64943 4.69816 8.65843 4.66473C8.66941 4.62595 8.67828 4.58661 8.685 4.54688C8.67015 4.5586 8.65454 4.56934 8.63829 4.57902C8.4822 4.66169 8.31419 4.71953 8.14029 4.75045H8.13257L8.08157 4.75859L8.09057 4.75473L6.957 4.87902L6.94843 4.89059Z"
|
||||
fill="url(#paint5_linear_2061_4195)"
|
||||
/>
|
||||
<path
|
||||
d="M8.22475 2.59871C8.12404 2.75343 8.01389 2.92914 7.89561 3.12843L7.87675 3.16014C7.77446 3.33157 7.66604 3.52114 7.55147 3.72886C7.45261 3.90771 7.34989 4.10014 7.24332 4.30614C7.15018 4.48586 7.05418 4.67671 6.95532 4.87871L8.08889 4.75443C8.33914 4.65567 8.55501 4.48583 8.70989 4.26586C8.74804 4.211 8.78618 4.15357 8.82432 4.09443C8.94089 3.91271 9.05489 3.71257 9.15689 3.51371C9.25018 3.33322 9.33429 3.14813 9.40889 2.95914C9.44758 2.86102 9.48092 2.76087 9.50875 2.65914C9.52932 2.58028 9.54561 2.50571 9.55804 2.43457C9.11675 2.51236 8.67172 2.56715 8.22475 2.59871Z"
|
||||
fill="url(#paint6_linear_2061_4195)"
|
||||
/>
|
||||
<path
|
||||
d="M7.49234 6.50635C7.46963 6.5102 7.44648 6.51406 7.42334 6.51706C7.44648 6.51406 7.47134 6.5102 7.49234 6.50635Z"
|
||||
fill="#BE202E"
|
||||
/>
|
||||
<path
|
||||
opacity="0.35"
|
||||
d="M7.49234 6.50635C7.46963 6.5102 7.44648 6.51406 7.42334 6.51706C7.44648 6.51406 7.47134 6.5102 7.49234 6.50635Z"
|
||||
fill="#BE202E"
|
||||
/>
|
||||
<path
|
||||
d="M7.49234 6.50635C7.46963 6.5102 7.44648 6.51406 7.42334 6.51706C7.44648 6.51406 7.47134 6.5102 7.49234 6.50635Z"
|
||||
fill="url(#paint7_linear_2061_4195)"
|
||||
/>
|
||||
<defs>
|
||||
<linearGradient
|
||||
id="paint0_linear_2061_4195"
|
||||
x1="7.26579"
|
||||
y1="1.16584"
|
||||
x2="9.7782"
|
||||
y2="0.46843"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop stopColor="#F69923" />
|
||||
<stop offset="0.312" stopColor="#F79A23" />
|
||||
<stop offset="0.838" stopColor="#E97826" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="paint1_linear_2061_4195"
|
||||
x1="1.76109"
|
||||
y1="12.4382"
|
||||
x2="6.87606"
|
||||
y2="1.48267"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop offset="0.323" stopColor="#9E2064" />
|
||||
<stop offset="0.63" stopColor="#C92037" />
|
||||
<stop offset="0.751" stopColor="#CD2335" />
|
||||
<stop offset="1" stopColor="#E97826" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="paint2_linear_2061_4195"
|
||||
x1="3.47784"
|
||||
y1="11.6287"
|
||||
x2="6.5258"
|
||||
y2="5.10046"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop stopColor="#282662" />
|
||||
<stop offset="0.095" stopColor="#662E8D" />
|
||||
<stop offset="0.788" stopColor="#9F2064" />
|
||||
<stop offset="0.949" stopColor="#CD2032" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="paint3_linear_2061_4195"
|
||||
x1="1.94596"
|
||||
y1="11.7748"
|
||||
x2="7.06094"
|
||||
y2="0.819304"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop offset="0.323" stopColor="#9E2064" />
|
||||
<stop offset="0.63" stopColor="#C92037" />
|
||||
<stop offset="0.751" stopColor="#CD2335" />
|
||||
<stop offset="1" stopColor="#E97826" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="paint4_linear_2061_4195"
|
||||
x1="2.46768"
|
||||
y1="11.0455"
|
||||
x2="5.15578"
|
||||
y2="5.28798"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop stopColor="#282662" />
|
||||
<stop offset="0.095" stopColor="#662E8D" />
|
||||
<stop offset="0.788" stopColor="#9F2064" />
|
||||
<stop offset="0.949" stopColor="#CD2032" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="paint5_linear_2061_4195"
|
||||
x1="3.08048"
|
||||
y1="12.3044"
|
||||
x2="8.19546"
|
||||
y2="1.3489"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop offset="0.323" stopColor="#9E2064" />
|
||||
<stop offset="0.63" stopColor="#C92037" />
|
||||
<stop offset="0.751" stopColor="#CD2335" />
|
||||
<stop offset="1" stopColor="#E97826" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="paint6_linear_2061_4195"
|
||||
x1="2.70679"
|
||||
y1="12.9581"
|
||||
x2="7.82195"
|
||||
y2="2.00223"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop offset="0.323" stopColor="#9E2064" />
|
||||
<stop offset="0.63" stopColor="#C92037" />
|
||||
<stop offset="0.751" stopColor="#CD2335" />
|
||||
<stop offset="1" stopColor="#E97826" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="paint7_linear_2061_4195"
|
||||
x1="3.41759"
|
||||
y1="12.4619"
|
||||
x2="8.53277"
|
||||
y2="1.50629"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop offset="0.323" stopColor="#9E2064" />
|
||||
<stop offset="0.63" stopColor="#C92037" />
|
||||
<stop offset="0.751" stopColor="#CD2335" />
|
||||
<stop offset="1" stopColor="#E97826" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
);
|
||||
}
|
28
frontend/src/assets/CustomIcons/DockerIcon.tsx
Normal file
28
frontend/src/assets/CustomIcons/DockerIcon.tsx
Normal file
@ -0,0 +1,28 @@
|
||||
export default function DockerIcon(): JSX.Element {
|
||||
return (
|
||||
<svg
|
||||
width="14"
|
||||
height="14"
|
||||
viewBox="0 0 14 14"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<g clipPath="url(#clip0_2061_4220)">
|
||||
<path
|
||||
d="M13.574 5.82107C13.2652 5.60679 12.5575 5.52644 12.0042 5.63358C11.9398 5.09788 11.6439 4.62915 11.1292 4.21399L10.8332 3.99971L10.6274 4.30774C10.37 4.70951 10.2413 5.27198 10.2799 5.80768C10.2928 5.99517 10.3571 6.32998 10.5502 6.62461C10.37 6.73175 9.99686 6.86567 9.50789 6.86567H0.204685L0.17895 6.97281C0.0888774 7.5085 0.0888774 9.18254 1.14401 10.4682C1.9418 11.4458 3.12561 11.9414 4.68258 11.9414C8.05386 11.9414 10.5502 10.3209 11.7211 7.38797C12.1843 7.40136 13.1751 7.38797 13.677 6.38354C13.6898 6.35676 13.7156 6.30319 13.8056 6.10231L13.8571 5.99517L13.574 5.82107ZM7.6421 2.04443H6.22668V3.38367H7.6421V2.04443ZM7.6421 3.65151H6.22668V4.99074H7.6421V3.65151ZM5.96933 3.65151H4.5539V4.99074H5.96933V3.65151ZM4.29655 3.65151H2.88113V4.99074H4.29655V3.65151ZM2.62378 5.25859H1.20835V6.59782H2.62378V5.25859ZM4.29655 5.25859H2.88113V6.59782H4.29655V5.25859ZM5.96933 5.25859H4.5539V6.59782H5.96933V5.25859ZM7.6421 5.25859H6.22668V6.59782H7.6421V5.25859ZM9.31488 5.25859H7.89945V6.59782H9.31488V5.25859Z"
|
||||
fill="#2396ED"
|
||||
/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_2061_4220">
|
||||
<rect
|
||||
width="13.7143"
|
||||
height="13.7143"
|
||||
fill="white"
|
||||
transform="translate(0.142822 0.143066)"
|
||||
/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
);
|
||||
}
|
36
frontend/src/assets/CustomIcons/ElasticSearchIcon.tsx
Normal file
36
frontend/src/assets/CustomIcons/ElasticSearchIcon.tsx
Normal file
@ -0,0 +1,36 @@
|
||||
export default function ElasticSearchIcon(): JSX.Element {
|
||||
return (
|
||||
<svg
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 16 16"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M6.38041 6.94508L9.70241 8.45858L13.0524 5.52158C13.1019 5.27892 13.1255 5.03171 13.1229 4.78408C13.1236 3.9846 12.8681 3.20592 12.3941 2.56216C11.92 1.9184 11.2522 1.44339 10.4885 1.20675C9.72487 0.970101 8.9055 0.984259 8.15047 1.24714C7.39544 1.51003 6.74446 2.00782 6.29291 2.66758L5.73291 5.56008L6.38041 6.94508Z"
|
||||
fill="#FED10A"
|
||||
/>
|
||||
<path
|
||||
d="M2.943 10.4593C2.89356 10.7062 2.86994 10.9575 2.8725 11.2093C2.87361 12.012 3.13177 12.7932 3.60915 13.4385C4.08654 14.0838 4.75804 14.5592 5.52528 14.7952C6.29252 15.0311 7.11514 15.0151 7.87262 14.7495C8.63009 14.4839 9.28259 13.9827 9.7345 13.3193L10.2845 10.4398L9.55 9.02928L6.215 7.50928L2.943 10.4593Z"
|
||||
fill="#24BBB1"
|
||||
/>
|
||||
<path
|
||||
d="M2.92394 4.71302L5.19994 5.25002L5.69994 2.66552C5.39077 2.43015 5.01365 2.30134 4.62509 2.29839C4.23654 2.29544 3.8575 2.41852 3.54479 2.64916C3.23208 2.8798 3.00256 3.20559 2.89063 3.57768C2.77869 3.94978 2.79039 4.34813 2.92394 4.71302Z"
|
||||
fill="#EF5098"
|
||||
/>
|
||||
<path
|
||||
d="M2.72503 5.2583C2.23266 5.42016 1.80253 5.73058 1.49379 6.14687C1.18505 6.56317 1.01286 7.0649 1.00091 7.58305C0.988962 8.1012 1.13784 8.61033 1.42706 9.04041C1.71628 9.4705 2.13164 9.80042 2.61603 9.9848L5.81003 7.0998L5.22653 5.8498L2.72503 5.2583Z"
|
||||
fill="#17A8E0"
|
||||
/>
|
||||
<path
|
||||
d="M10.312 13.3197C10.6209 13.5543 10.9975 13.6825 11.3853 13.6851C11.7732 13.6877 12.1514 13.5646 12.4634 13.3342C12.7755 13.1038 13.0044 12.7785 13.116 12.407C13.2276 12.0356 13.2159 11.6379 13.0825 11.2737L10.812 10.7412L10.312 13.3197Z"
|
||||
fill="#93C83E"
|
||||
/>
|
||||
<path
|
||||
d="M10.7735 10.145L13.2735 10.7285C13.7666 10.5672 14.1976 10.257 14.507 9.84058C14.8165 9.42415 14.9891 8.922 15.0013 8.40333C15.0134 7.88467 14.8643 7.375 14.5747 6.94457C14.2851 6.51414 13.8691 6.18413 13.384 6L10.1135 8.8665L10.7735 10.145Z"
|
||||
fill="#0779A1"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
}
|
18
frontend/src/assets/CustomIcons/GrafanaIcon.tsx
Normal file
18
frontend/src/assets/CustomIcons/GrafanaIcon.tsx
Normal file
File diff suppressed because one or more lines are too long
27
frontend/src/assets/CustomIcons/HerokuIcon.tsx
Normal file
27
frontend/src/assets/CustomIcons/HerokuIcon.tsx
Normal file
@ -0,0 +1,27 @@
|
||||
function HerokuIcon(): JSX.Element {
|
||||
return (
|
||||
<svg
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 16 16"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<g clipPath="url(#clip0_2061_4245)">
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
d="M13.6285 0H2.10787C1.31283 0 0.666748 0.644312 0.666748 1.44112V14.5602C0.666748 15.3557 1.31283 16 2.10787 16H13.6285C14.4237 16 15.0678 15.3557 15.0678 14.5602V1.44112C15.0678 0.644312 14.4237 0 13.6285 0ZM14.2677 14.5602C14.2677 14.9135 13.9815 15.1994 13.6285 15.1994H2.10787C1.75506 15.1994 1.46688 14.9135 1.46688 14.5602V1.44112C1.46688 1.08654 1.75506 0.800133 2.10787 0.800133H13.6285C13.9815 0.800133 14.2677 1.08654 14.2677 1.44112V14.5602ZM4.26699 13.6009L6.06823 12.0002L4.26699 10.3999V13.6009ZM10.7719 7.11485C10.4481 6.78927 9.8569 6.40038 8.86819 6.40038C7.78342 6.40038 6.66611 6.68302 5.86752 6.94177V2.40082H4.26704V9.33907L5.39807 8.82667C5.41688 8.81826 7.24025 8.00086 8.86819 8.00086C9.68027 8.00086 9.86022 8.44796 9.86885 8.82158V13.6009H11.4676V8.801C11.4693 8.6983 11.4592 7.81051 10.7719 7.11485ZM8.66761 5.00027H10.2681C10.9912 4.1811 11.3597 3.30903 11.4677 2.40066H9.86881C9.69063 3.30726 9.29643 4.17424 8.66761 5.00027Z"
|
||||
fill="#6762A6"
|
||||
/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_2061_4245">
|
||||
<rect width="16" height="16" fill="white" />
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export default HerokuIcon;
|
82
frontend/src/assets/CustomIcons/JuiceBoxIcon.tsx
Normal file
82
frontend/src/assets/CustomIcons/JuiceBoxIcon.tsx
Normal file
@ -0,0 +1,82 @@
|
||||
function JuiceBoxIcon(): JSX.Element {
|
||||
return (
|
||||
<svg
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 16 16"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M7.15412 6.3041L3.80207 4.64419C3.80207 4.64419 3.79429 4.48531 3.94429 4.40531C4.09539 4.32532 6.47082 3.35204 6.66192 3.35204C6.85302 3.35204 7.97185 3.56758 8.78625 3.74868C9.60065 3.92978 11.7528 4.39198 11.7628 4.53308C11.7728 4.67419 9.56066 6.93629 9.56066 6.93629L7.15412 6.3041Z"
|
||||
fill="#C3FECE"
|
||||
/>
|
||||
<path
|
||||
d="M10.2117 5.2686C10.2117 5.2686 8.80626 4.85529 7.43855 4.53419C6.80192 4.3842 4.93091 3.992 4.93091 3.992C4.93091 3.992 5.12979 3.90534 5.27978 3.84201C5.43199 3.77868 5.5531 3.74535 5.5531 3.74535C5.5531 3.74535 6.77303 3.98867 7.67965 4.18199C8.90625 4.44309 10.6228 4.95528 10.6628 5.03639C10.7039 5.11639 10.2117 5.2686 10.2117 5.2686Z"
|
||||
fill="white"
|
||||
/>
|
||||
<path
|
||||
d="M7.48978 4.87973C7.19091 5.0875 7.48423 5.21305 8.03531 5.34193C8.63861 5.48303 9.09858 5.6908 9.48634 5.46526C9.83632 5.26304 8.9797 5.04306 8.58639 4.95862C8.28641 4.89418 7.72088 4.71974 7.48978 4.87973Z"
|
||||
fill="#ACB1B2"
|
||||
/>
|
||||
<path
|
||||
d="M8.58521 5.29749C8.58299 5.29749 8.59521 2.86317 8.59521 2.77317C8.59521 2.68318 8.49411 2.60207 8.60521 2.41097C8.71632 2.21987 9.00741 2.23987 9.00741 2.23987C9.00741 2.23987 10.2151 1.88878 10.6973 1.76767C11.1795 1.64657 12.2461 1.36547 12.2461 1.36547L12.3172 1.97544C12.3172 1.97544 11.1995 2.29098 10.6462 2.43208C10.0929 2.57319 9.33851 2.80428 9.33851 2.80428L9.2274 2.90539L9.25962 5.28415C9.25962 5.28415 9.1563 5.3997 8.9252 5.3997C8.70076 5.39859 8.58521 5.29749 8.58521 5.29749Z"
|
||||
fill="#FFD816"
|
||||
/>
|
||||
<path
|
||||
d="M12.0472 1.67768C12.0561 1.86767 12.1572 1.98544 12.2772 1.98544C12.3972 1.98544 12.4794 1.83211 12.4616 1.63212C12.4427 1.43325 12.3138 1.34214 12.2038 1.3788C12.0939 1.41547 12.0405 1.5388 12.0472 1.67768Z"
|
||||
fill="#FEB804"
|
||||
/>
|
||||
<path
|
||||
d="M9.22962 2.96094C9.20851 2.96094 9.1874 2.95539 9.16851 2.94206L8.59187 2.55763C8.54076 2.52319 8.52743 2.4543 8.56076 2.4032C8.59521 2.35209 8.66298 2.33764 8.7152 2.37209L9.29184 2.75651C9.34294 2.79095 9.35628 2.85984 9.32295 2.91095C9.30072 2.94317 9.26517 2.96094 9.22962 2.96094Z"
|
||||
fill="#FEB804"
|
||||
/>
|
||||
<path
|
||||
d="M9.31847 2.86761C9.28514 2.86761 9.25181 2.85206 9.22959 2.82317C9.08182 2.62651 8.91294 2.40319 8.88294 2.36875C8.84516 2.33208 8.83739 2.27209 8.86739 2.22653C8.90183 2.17542 8.9696 2.16098 9.02182 2.19542C9.04182 2.20876 9.05293 2.21653 9.40736 2.68873C9.44402 2.73762 9.41069 2.80761 9.3618 2.84428C9.34181 2.85983 9.34181 2.86761 9.31847 2.86761Z"
|
||||
fill="#FEB804"
|
||||
/>
|
||||
<path
|
||||
d="M9.18298 3.05427C9.17742 3.05427 9.17187 3.05427 9.1652 3.05316L8.60968 2.96539C8.54968 2.95539 8.50746 2.89872 8.51746 2.83873C8.52746 2.77873 8.58301 2.73651 8.64412 2.74651L9.19965 2.83428C9.25964 2.84428 9.31186 2.85539 9.30186 2.9165C9.29408 2.96983 9.23742 3.05427 9.18298 3.05427Z"
|
||||
fill="#FEB804"
|
||||
/>
|
||||
<path
|
||||
d="M3.79761 4.63197C3.97426 4.63197 5.39197 5.00417 6.12304 5.13861C6.85411 5.27304 9.06177 5.79524 9.13621 5.80968C9.21066 5.82413 9.19621 6.77963 9.19621 6.77963C9.19621 6.77963 10.2406 12.1793 10.2106 12.4338C10.1806 12.6871 9.49508 14.6414 9.49508 14.6414C9.49508 14.6414 9.09177 14.7014 8.06294 14.4026C7.0341 14.1037 4.31869 13.3582 4.15426 13.1493C3.98982 12.9404 4.09426 10.1806 4.00537 8.48065C3.91427 6.77963 3.79761 4.63197 3.79761 4.63197Z"
|
||||
fill="#79DD8A"
|
||||
/>
|
||||
<path
|
||||
d="M12.0294 13.2682C12.086 13.0804 11.7705 11.0672 11.7261 9.21728C11.6816 7.36849 11.8405 4.5553 11.7638 4.53308C11.6872 4.51197 9.13621 5.80968 9.13621 5.80968C9.13621 5.80968 9.1251 7.64514 9.15621 9.37616C9.1962 11.616 9.37508 14.5814 9.49396 14.6414C9.61285 14.7014 10.645 14.1237 10.9561 13.9548C11.5072 13.6559 11.9849 13.4182 12.0294 13.2682Z"
|
||||
fill="#02AB46"
|
||||
/>
|
||||
<path
|
||||
d="M10.204 6.65964L10.2095 5.26638L10.6662 5.04195L10.6795 6.56964L10.4684 6.93962L10.204 6.65964Z"
|
||||
fill="#DBDFE1"
|
||||
/>
|
||||
<path
|
||||
d="M6.16088 7.1485C5.17427 7.04295 4.49097 7.9229 4.5443 9.06951C4.60763 10.4239 5.4998 11.4216 6.46531 11.6205C7.43081 11.8194 8.36632 11.4005 8.38632 9.91947C8.40854 8.37621 7.05194 7.24294 6.16088 7.1485Z"
|
||||
fill="#FEFEFD"
|
||||
/>
|
||||
<path
|
||||
d="M6.81861 8.3851C6.81861 8.3851 6.61862 7.71292 6.01754 7.89179C5.41646 8.07067 5.09092 9.37838 6.11087 9.96724C7.09082 10.5328 7.99299 9.49504 7.67745 8.82286C7.3908 8.20956 6.81861 8.3851 6.81861 8.3851Z"
|
||||
fill="#EF5B44"
|
||||
/>
|
||||
<path
|
||||
d="M6.58968 7.56292C6.52191 7.58737 6.46858 7.86402 6.53968 8.12845C6.59302 8.32622 6.76301 8.59731 6.86189 8.57954C6.94077 8.56509 7.00077 8.254 6.92633 7.9929C6.83967 7.69181 6.66857 7.53404 6.58968 7.56292Z"
|
||||
fill="#B8CF17"
|
||||
/>
|
||||
<path
|
||||
d="M6.57531 9.12284C6.2931 9.03729 5.97534 10.0072 6.54642 10.6116C7.17639 11.2772 7.9119 10.6905 7.89191 10.5617C7.86302 10.3828 7.36971 10.2539 7.12639 9.9539C6.88307 9.6517 6.73974 9.17284 6.57531 9.12284Z"
|
||||
fill="#FD8F01"
|
||||
/>
|
||||
<path
|
||||
d="M5.67307 8.9584C5.67307 8.9584 5.72973 8.53398 5.33642 8.55731C4.95644 8.58065 5.04977 9.02951 5.04977 9.02951C5.04977 9.02951 4.73312 9.06729 4.78645 9.41838C4.82645 9.68392 5.10643 9.68059 5.10643 9.68059C5.10643 9.68059 4.79867 9.80169 4.93866 10.1528C5.0631 10.465 5.34975 10.3394 5.34975 10.3394C5.34975 10.3394 5.2442 10.6439 5.49308 10.805C5.70307 10.9405 5.88639 10.8261 5.88639 10.8261C5.88639 10.8261 5.87528 11.0861 6.16526 11.1338C6.51636 11.1916 6.66857 10.7639 6.41858 10.5661C6.22859 10.4161 6.05638 10.5328 6.05638 10.5328C6.05638 10.5328 6.09193 10.4494 6.0586 10.3539C6.03638 10.2917 6.00083 10.2683 6.00083 10.2683C6.00083 10.2683 6.30859 10.2394 6.25859 9.88947C6.2086 9.53837 5.90083 9.5817 5.90083 9.5817C5.90083 9.5817 6.07971 9.43838 6.00861 9.18061C5.93861 8.92174 5.67307 8.9584 5.67307 8.9584Z"
|
||||
fill="#A281D0"
|
||||
/>
|
||||
<path
|
||||
d="M10.5205 7.04739C10.265 7.04739 9.17503 5.96412 9.16503 5.95301C9.12504 5.90634 9.13059 5.83635 9.17726 5.79635C9.22392 5.75635 9.29281 5.76191 9.33391 5.80746C9.40391 5.88523 10.2372 6.65853 10.4772 6.80741C10.6327 6.5752 11.1383 5.55858 11.5727 4.64085C11.5994 4.5853 11.666 4.56197 11.7205 4.58752C11.776 4.61419 11.7993 4.67974 11.7738 4.73529C10.7027 6.99851 10.5972 7.02851 10.5405 7.04406C10.5339 7.0474 10.5272 7.04739 10.5205 7.04739Z"
|
||||
fill="#2D802D"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export default JuiceBoxIcon;
|
22
frontend/src/assets/CustomIcons/KubernetesIcon.tsx
Normal file
22
frontend/src/assets/CustomIcons/KubernetesIcon.tsx
Normal file
File diff suppressed because one or more lines are too long
38
frontend/src/assets/CustomIcons/MagicBallIcon.tsx
Normal file
38
frontend/src/assets/CustomIcons/MagicBallIcon.tsx
Normal file
@ -0,0 +1,38 @@
|
||||
function MagicBallIcon(): JSX.Element {
|
||||
return (
|
||||
<svg
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 16 16"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M13.2883 12.3928C14.4283 11.0451 15.4015 8.62412 14.396 5.84427C13.9061 4.49101 13.3528 3.73549 12.7084 3.25552C12.3162 2.96331 10.7496 2.2278 8.67524 2.54889C7.15865 2.78443 5.1332 3.74105 3.8166 5.29763C2.54 6.80978 1.5545 8.00416 1.50895 9.1641C1.45006 10.6562 2.57445 11.9384 2.71111 12.1462C2.95443 12.515 4.61434 14.6238 7.73306 14.7316C10.4862 14.826 12.2795 13.5861 13.2883 12.3928Z"
|
||||
fill="#403D3E"
|
||||
/>
|
||||
<path
|
||||
d="M4.04763 2.43331C2.61104 3.45881 0.996679 5.55647 1.28666 8.49854C1.42777 9.93069 1.77997 10.7995 2.2855 11.4228C2.59326 11.8028 3.92875 12.9328 6.02086 13.0994C8.12964 13.2672 9.73288 12.795 11.4072 11.6306C14.8104 9.26295 14.3093 5.68313 14.1638 5.26649C14.0182 4.84984 12.9283 2.39664 9.93176 1.52446C7.28746 0.755617 5.31867 1.52446 4.04763 2.43331Z"
|
||||
fill="#5E6367"
|
||||
/>
|
||||
<path
|
||||
d="M6.57189 2.63219C4.99308 2.57553 3.48427 3.81213 3.33872 5.33871C3.19318 6.86419 4.13757 8.02635 5.6086 8.263C7.07964 8.49855 8.78066 7.60526 9.12286 5.73425C9.47618 3.80657 8.07958 2.68663 6.57189 2.63219Z"
|
||||
fill="white"
|
||||
/>
|
||||
<path
|
||||
d="M7.05971 5.24541C7.05971 5.24541 7.43969 5.16653 7.51524 4.60655C7.58968 4.05547 7.31636 3.5855 6.67862 3.41662C5.98532 3.23329 5.51535 3.61438 5.39313 4.01547C5.22314 4.57322 5.47424 4.83876 5.47424 4.83876C5.47424 4.83876 4.79427 5.00209 4.73983 5.80427C4.68872 6.5609 5.20536 6.96754 5.72422 7.09198C6.3653 7.24642 7.09193 7.07087 7.2697 6.25313C7.41747 5.57984 7.05971 5.24541 7.05971 5.24541Z"
|
||||
fill="#303030"
|
||||
/>
|
||||
<path
|
||||
d="M5.99081 4.22544C5.92971 4.45543 6.05192 4.67764 6.29191 4.73874C6.55079 4.8043 6.78633 4.71875 6.84966 4.45431C6.90521 4.21988 6.79411 4.01323 6.52079 3.94656C6.29635 3.89101 6.05748 3.97434 5.99081 4.22544Z"
|
||||
fill="white"
|
||||
/>
|
||||
<path
|
||||
d="M6.18643 5.36871C5.89533 5.27871 5.51091 5.39093 5.4498 5.78202C5.38869 6.17311 5.62313 6.3731 5.92978 6.42865C6.23643 6.4842 6.52641 6.3231 6.58308 6.00978C6.63863 5.69758 6.47641 5.45759 6.18643 5.36871Z"
|
||||
fill="white"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export default MagicBallIcon;
|
68
frontend/src/assets/CustomIcons/MongoDBIcon.tsx
Normal file
68
frontend/src/assets/CustomIcons/MongoDBIcon.tsx
Normal file
@ -0,0 +1,68 @@
|
||||
export default function MongoDBIcon(): JSX.Element {
|
||||
return (
|
||||
<svg
|
||||
width="14"
|
||||
height="14"
|
||||
viewBox="0 0 14 14"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M7.26568 13.0004L6.94382 12.8937C6.94382 12.8937 6.98668 11.2651 6.39739 11.1507C6.01168 10.7015 6.45439 -8.02403 7.86439 11.0868C7.59687 11.2225 7.39217 11.4564 7.29311 11.7395C7.24001 12.1577 7.23082 12.5803 7.26568 13.0004Z"
|
||||
fill="url(#paint0_linear_2061_4238)"
|
||||
/>
|
||||
<path
|
||||
d="M7.43957 11.4272C8.29654 10.7821 8.95282 9.90701 9.33214 8.90369C9.71147 7.90037 9.79826 6.81 9.58243 5.75931C8.95243 2.98002 7.46058 2.06631 7.29986 1.71745C7.1612 1.50022 7.04284 1.27068 6.94629 1.03174L7.065 8.7756C7.065 8.7756 6.819 11.1422 7.43957 11.4272Z"
|
||||
fill="url(#paint1_linear_2061_4238)"
|
||||
/>
|
||||
<path
|
||||
d="M6.78015 11.5296C6.78015 11.5296 4.15687 9.74286 4.30858 6.58214C4.32273 5.62928 4.54121 4.69054 4.94926 3.82935C5.3573 2.96816 5.94542 2.20456 6.67387 1.59014C6.759 1.51782 6.82663 1.42715 6.87168 1.32493C6.91674 1.22272 6.93805 1.11163 6.93401 1C7.0973 1.35143 7.07072 6.247 7.08787 6.81957C7.1543 9.04686 6.96401 11.1091 6.78015 11.5296Z"
|
||||
fill="url(#paint2_linear_2061_4238)"
|
||||
/>
|
||||
<defs>
|
||||
<linearGradient
|
||||
id="paint0_linear_2061_4238"
|
||||
x1="5.1021"
|
||||
y1="7.10853"
|
||||
x2="8.80138"
|
||||
y2="8.36386"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop offset="0.231" stopColor="#999875" />
|
||||
<stop offset="0.563" stopColor="#9B9977" />
|
||||
<stop offset="0.683" stopColor="#A09F7E" />
|
||||
<stop offset="0.768" stopColor="#A9A889" />
|
||||
<stop offset="0.837" stopColor="#B7B69A" />
|
||||
<stop offset="0.896" stopColor="#C9C7B0" />
|
||||
<stop offset="0.948" stopColor="#DEDDCB" />
|
||||
<stop offset="0.994" stopColor="#F8F6EB" />
|
||||
<stop offset="1" stopColor="#FBF9EF" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="paint1_linear_2061_4238"
|
||||
x1="6.45855"
|
||||
y1="0.976404"
|
||||
x2="8.09399"
|
||||
y2="11.1888"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop stopColor="#48A547" />
|
||||
<stop offset="1" stopColor="#3F9143" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="paint2_linear_2061_4238"
|
||||
x1="4.08293"
|
||||
y1="6.89501"
|
||||
x2="8.47176"
|
||||
y2="5.42519"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop stopColor="#41A247" />
|
||||
<stop offset="0.352" stopColor="#4BA74B" />
|
||||
<stop offset="0.956" stopColor="#67B554" />
|
||||
<stop offset="1" stopColor="#69B655" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
);
|
||||
}
|
28
frontend/src/assets/CustomIcons/MySQLIcon.tsx
Normal file
28
frontend/src/assets/CustomIcons/MySQLIcon.tsx
Normal file
File diff suppressed because one or more lines are too long
22
frontend/src/assets/CustomIcons/NginxIcon.tsx
Normal file
22
frontend/src/assets/CustomIcons/NginxIcon.tsx
Normal file
@ -0,0 +1,22 @@
|
||||
function NginxIcon(): JSX.Element {
|
||||
return (
|
||||
<svg
|
||||
width="14"
|
||||
height="14"
|
||||
viewBox="0 0 14 14"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M6.97775 1H7.00561C7.14837 1.068 7.28743 1.14353 7.42218 1.22629C8.97332 2.11829 10.5245 3.01057 12.0756 3.90314C12.1356 3.93517 12.1846 3.9845 12.2163 4.04472C12.2479 4.10493 12.2607 4.17327 12.253 4.24086C12.2496 6.12186 12.253 8.00243 12.2509 9.88257C12.2307 9.9723 12.1759 10.0504 12.0983 10.0999C10.4489 11.0496 8.79932 11.9987 7.14961 12.9473C7.10963 12.9778 7.06144 12.9956 7.01125 12.9984C6.96106 13.0012 6.91118 12.9889 6.86803 12.9631C5.21661 12.0163 3.56675 11.0677 1.91846 10.1174C1.86489 10.0921 1.82003 10.0515 1.78952 10.0007C1.75901 9.94988 1.74423 9.89119 1.74703 9.832C1.74703 7.95143 1.74703 6.071 1.74703 4.19071C1.74299 4.13178 1.75661 4.07299 1.78616 4.02184C1.8157 3.97069 1.85983 3.92951 1.91289 3.90357C3.46203 3.01271 5.01118 2.12129 6.56032 1.22929C6.69832 1.15043 6.83375 1.06686 6.97775 1Z"
|
||||
fill="#019639"
|
||||
/>
|
||||
<path
|
||||
d="M3.90007 4.65924C3.90007 6.21039 3.90007 7.76167 3.90007 9.3131C3.89809 9.39901 3.91326 9.48446 3.94468 9.56445C3.9761 9.64444 4.02315 9.71736 4.08307 9.77896C4.19794 9.89243 4.34824 9.96309 4.5089 9.97916C4.66956 9.99522 4.83087 9.95572 4.96593 9.86724C5.05634 9.80581 5.13035 9.72321 5.18152 9.62662C5.23269 9.53003 5.25946 9.4224 5.2595 9.3131C5.2595 8.19024 5.25736 7.06739 5.2595 5.94453C6.28322 7.17024 7.30907 8.39424 8.33707 9.61653C8.4799 9.76122 8.65676 9.86772 8.85145 9.92628C9.04614 9.98484 9.25242 9.99357 9.45136 9.95167C9.59184 9.924 9.71973 9.85198 9.81624 9.74622C9.91275 9.64045 9.97278 9.50652 9.9875 9.3641C9.98979 7.78096 9.98979 6.19796 9.9875 4.6151C9.97275 4.44614 9.8952 4.28884 9.77016 4.17425C9.64512 4.05966 9.48168 3.99609 9.31207 3.99609C9.14247 3.99609 8.97902 4.05966 8.85399 4.17425C8.72895 4.28884 8.6514 4.44614 8.63665 4.6151C8.63665 5.75596 8.62979 6.89553 8.63665 8.03596C7.63122 6.85053 6.63822 5.65481 5.63665 4.4651C5.5046 4.29578 5.32979 4.16474 5.13023 4.08549C4.93067 4.00624 4.71359 3.98165 4.50136 4.01424C4.34059 4.03214 4.19154 4.10704 4.08124 4.22536C3.97093 4.34369 3.90666 4.49761 3.90007 4.65924Z"
|
||||
fill="white"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export default NginxIcon;
|
40
frontend/src/assets/CustomIcons/PostgreSQLIcon.tsx
Normal file
40
frontend/src/assets/CustomIcons/PostgreSQLIcon.tsx
Normal file
File diff suppressed because one or more lines are too long
60
frontend/src/assets/CustomIcons/RedisIcon.tsx
Normal file
60
frontend/src/assets/CustomIcons/RedisIcon.tsx
Normal file
@ -0,0 +1,60 @@
|
||||
export default function RedisIcon(): JSX.Element {
|
||||
return (
|
||||
<svg
|
||||
width="14"
|
||||
height="14"
|
||||
viewBox="0 0 14 14"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<g clipPath="url(#clip0_2061_4282)">
|
||||
<path
|
||||
d="M13.3198 10.158C12.5879 10.5395 8.79654 12.0984 7.98938 12.5192C7.18221 12.94 6.73382 12.936 6.09616 12.6312C5.45855 12.3263 1.42388 10.6966 0.697072 10.3492C0.333858 10.1756 0.142822 10.029 0.142822 9.8906V8.50438C0.142822 8.50438 5.3955 7.3609 6.24348 7.05667C7.09141 6.75244 7.38563 6.74145 8.10723 7.00578C8.82895 7.2702 13.1439 8.0487 13.8571 8.30992L13.8568 9.67653C13.8569 9.81356 13.6923 9.96388 13.3198 10.158Z"
|
||||
fill="#912626"
|
||||
/>
|
||||
<path
|
||||
d="M13.3195 8.77972C12.5877 9.16104 8.79642 10.72 7.98926 11.1407C7.18215 11.5616 6.73376 11.5575 6.09615 11.2527C5.45849 10.9481 1.42397 9.31805 0.697221 8.97086C-0.029529 8.62345 -0.0447433 8.38436 0.66915 8.10482C1.38304 7.82518 5.39544 6.25098 6.24353 5.94675C7.09145 5.64263 7.38561 5.63154 8.10722 5.89597C8.82888 6.16029 12.5975 7.66034 13.3106 7.9215C14.024 8.18298 14.0513 8.39829 13.3195 8.77972Z"
|
||||
fill="#C6302B"
|
||||
/>
|
||||
<path
|
||||
d="M13.3198 7.91483C12.5879 8.29637 8.79654 9.85519 7.98938 10.2762C7.18221 10.6968 6.73382 10.6928 6.09616 10.388C5.4585 10.0833 1.42388 8.45338 0.697072 8.10597C0.333858 7.9324 0.142822 7.78604 0.142822 7.64756V6.26119C0.142822 6.26119 5.3955 5.11776 6.24348 4.81353C7.09141 4.50929 7.38563 4.49826 8.10723 4.76263C8.829 5.02701 13.144 5.80535 13.8571 6.06661L13.8568 7.43338C13.8569 7.57036 13.6923 7.72069 13.3198 7.91483Z"
|
||||
fill="#912626"
|
||||
/>
|
||||
<path
|
||||
d="M13.3195 6.53701C12.5877 6.91844 8.79642 8.47726 7.98926 8.89817C7.18215 9.31892 6.73376 9.3148 6.09615 9.00998C5.45849 8.70537 1.42397 7.07541 0.697221 6.72816C-0.029529 6.38085 -0.0447433 6.14171 0.66915 5.86207C1.38304 5.58258 5.39549 4.00828 6.24353 3.7041C7.09145 3.39992 7.38561 3.38889 8.10722 3.65326C8.82888 3.91758 12.5975 5.41753 13.3106 5.6788C14.024 5.94023 14.0513 6.15558 13.3195 6.53701Z"
|
||||
fill="#C6302B"
|
||||
/>
|
||||
<path
|
||||
d="M13.3198 5.58855C12.5879 5.96998 8.79654 7.5289 7.98938 7.94987C7.18221 8.37062 6.73382 8.36649 6.09616 8.06167C5.4585 7.75701 1.42388 6.12705 0.697072 5.7798C0.333858 5.60607 0.142822 5.45965 0.142822 5.32133V3.9349C0.142822 3.9349 5.3955 2.79153 6.24348 2.48735C7.09141 2.18307 7.38563 2.17214 8.10723 2.43646C8.829 2.70083 13.144 3.47917 13.8571 3.74044L13.8568 5.10715C13.8569 5.24403 13.6923 5.39435 13.3198 5.58855Z"
|
||||
fill="#912626"
|
||||
/>
|
||||
<path
|
||||
d="M13.3195 4.21078C12.5876 4.59221 8.79639 6.15113 7.98923 6.57188C7.18212 6.99263 6.73373 6.98851 6.09612 6.68385C5.45852 6.37903 1.42394 4.74917 0.697248 4.40187C-0.0295558 4.05462 -0.0447165 3.81542 0.669123 3.53583C1.38302 3.2563 5.39546 1.68221 6.2435 1.37792C7.09143 1.07369 7.38559 1.06276 8.1072 1.32714C8.82886 1.59151 12.5975 3.09146 13.3106 3.35272C14.0239 3.61394 14.0513 3.82935 13.3195 4.21078Z"
|
||||
fill="#C6302B"
|
||||
/>
|
||||
<path
|
||||
d="M8.67572 2.86218L7.49661 2.9846L7.23267 3.61974L6.80635 2.91099L5.44483 2.78863L6.46076 2.42226L6.15594 1.85986L7.1071 2.23186L8.00378 1.93829L7.76142 2.51981L8.67572 2.86218ZM7.16228 5.94351L4.96172 5.03081L8.11494 4.54679L7.16228 5.94351ZM4.11138 3.21522C5.04219 3.21522 5.79674 3.50772 5.79674 3.86847C5.79674 4.22933 5.04219 4.52177 4.11138 4.52177C3.18058 4.52177 2.42603 4.22927 2.42603 3.86847C2.42603 3.50772 3.18058 3.21522 4.11138 3.21522Z"
|
||||
fill="white"
|
||||
/>
|
||||
<path
|
||||
d="M10.0693 3.0357L11.9355 3.77316L10.0709 4.50993L10.0693 3.0357Z"
|
||||
fill="#621B1C"
|
||||
/>
|
||||
<path
|
||||
d="M8.00464 3.85234L10.0693 3.03564L10.0709 4.50988L9.86844 4.58906L8.00464 3.85234Z"
|
||||
fill="#9A2928"
|
||||
/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_2061_4282">
|
||||
<rect
|
||||
width="13.7143"
|
||||
height="13.7143"
|
||||
fill="white"
|
||||
transform="translate(0.142822 0.143066)"
|
||||
/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
);
|
||||
}
|
110
frontend/src/assets/CustomIcons/TentIcon.tsx
Normal file
110
frontend/src/assets/CustomIcons/TentIcon.tsx
Normal file
@ -0,0 +1,110 @@
|
||||
function TentIcon(): JSX.Element {
|
||||
return (
|
||||
<svg
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 16 16"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M9.75393 9.01964L6.72187 8.90409L6.18079 14.0994C6.18079 14.0994 7.01185 14.0416 8.1118 14.0416C9.21174 14.0416 10.1395 14.1194 10.1395 14.1194L9.75393 9.01964Z"
|
||||
fill="#8A2E08"
|
||||
/>
|
||||
<path
|
||||
d="M3.65538 9.74625L2.9132 9.59182C2.9132 9.59182 2.73099 10.7684 2.56322 11.4973C2.39545 12.2261 2.11658 13.2116 2.11658 13.2116L3.88093 14.0882C3.88093 14.0882 5.47751 14.2282 5.50529 14.1727C5.53306 14.1171 6.70967 11.3851 6.70967 11.3851L6.98965 10.334L3.65538 9.74625Z"
|
||||
fill="#FF6110"
|
||||
/>
|
||||
<path
|
||||
d="M9.17395 10.3207L12.8715 9.55071L13.0393 9.99847C13.0393 9.99847 13.2826 11.0162 13.4171 11.5673C13.6337 12.4517 13.9126 13.2439 13.9126 13.2439L12.2538 14.2705L10.545 14.2149L9.17395 10.3207Z"
|
||||
fill="#FF6110"
|
||||
/>
|
||||
<path
|
||||
d="M2.90422 9.15296C2.90422 9.17852 2.92199 9.36406 2.9131 9.49961C2.90422 9.63516 2.87866 9.80182 2.87866 9.80182C2.87866 9.80182 2.97977 9.82404 3.0731 9.88403C3.16642 9.94403 3.35308 10.1729 3.35308 10.1729C3.35308 10.1729 3.71751 10.0385 3.93861 10.1218C4.27859 10.2496 4.42303 10.564 4.42303 10.564C4.42303 10.564 4.77078 10.2407 5.19632 10.3096C5.61185 10.3762 5.77407 10.7684 5.77407 10.7684C5.77407 10.7684 6.08627 10.5162 6.35959 10.5307C6.68291 10.5473 6.80179 10.7851 6.80179 10.7851L7.28621 9.80848L3.68529 9.15407H2.90422V9.15296Z"
|
||||
fill="#AF0D03"
|
||||
/>
|
||||
<path
|
||||
d="M1.93542 13.6716L2.13097 13.1539C2.13097 13.1539 2.99759 13.5783 3.62534 13.7649C4.25308 13.9516 4.99304 13.9772 4.99304 13.9772C4.99304 13.9772 4.98415 13.6838 4.71194 13.4716C4.43974 13.2594 4.00865 12.9028 4.00865 12.9028L4.59751 12.7061L4.92527 12.575L5.95299 13.7983L5.6219 14.6227C5.6219 14.6227 4.89971 14.716 3.74533 14.4194C2.59095 14.1227 1.93542 13.6716 1.93542 13.6716Z"
|
||||
fill="#C9C9C9"
|
||||
/>
|
||||
<path
|
||||
d="M9.104 9.63738C9.12067 9.70516 9.11289 10.2407 9.11289 10.2407L9.42732 10.5973C9.42732 10.5973 9.59731 10.3762 9.9884 10.394C10.3795 10.4107 10.5573 10.5896 10.5573 10.5896C10.5573 10.5896 10.795 10.3007 11.1183 10.2418C11.4417 10.1818 11.755 10.3518 11.755 10.3518C11.755 10.3518 11.9761 9.94403 12.2983 9.85071C12.6216 9.75738 13.0394 9.99959 13.0394 9.99959L12.8671 8.95075L9.69953 9.17852L9.104 9.63738Z"
|
||||
fill="#AF0D03"
|
||||
/>
|
||||
<path
|
||||
d="M13.8782 13.1372L14.0815 13.6549C14.0815 13.6549 13.4615 14.1394 12.6627 14.3605C11.8638 14.5816 10.7528 14.6704 10.7528 14.6704L10.4283 12.8494L11.5238 12.5183L12.1249 12.775C12.1249 12.775 11.5927 13.2227 11.4472 13.4283C11.1705 13.8172 11.2694 14.0638 11.2694 14.0638C11.2694 14.0638 12.1183 13.9872 12.7727 13.7072C13.4282 13.425 13.8782 13.1372 13.8782 13.1372Z"
|
||||
fill="#C9C9C9"
|
||||
/>
|
||||
<path
|
||||
d="M4.42432 12.8905C4.42432 12.8905 4.80096 13.0594 4.99318 13.1961C5.23094 13.3661 5.46871 13.6205 5.54537 14.0283C5.5987 14.3094 5.62203 14.6227 5.62203 14.6227C5.62203 14.6227 5.88535 14.826 6.4031 13.9605C6.92085 13.095 7.18417 11.8962 7.2275 11.0473C7.26972 10.1985 7.25306 10.0029 7.25306 10.0029L6.48865 11.2595L5.35871 12.2872L4.42432 12.8905Z"
|
||||
fill="#D92F0A"
|
||||
/>
|
||||
<path
|
||||
d="M7.25966 10.0074C7.25966 10.0074 7.34855 10.7784 6.30971 11.735C5.66419 12.3295 5.03422 12.7383 4.60314 12.9072C4.45537 12.965 4.32315 12.9417 4.22982 12.9405C3.92539 12.9361 3.98206 12.8839 4.15205 12.7972C4.35204 12.695 4.93312 12.4828 6.02084 11.4806C6.89079 10.6784 6.95079 9.87626 6.95079 9.87626L7.25855 9.7096V10.0074H7.25966Z"
|
||||
fill="#FFFEFF"
|
||||
/>
|
||||
<path
|
||||
d="M10.7949 14.666C10.7949 14.666 10.4127 14.8015 9.835 14.0205C9.25725 13.2394 9.07948 11.855 9.05393 11.4128C9.02837 10.9706 9.03726 10.1385 9.03726 10.1385L10.0994 11.4717C10.0994 11.4717 11.7471 12.7117 11.7216 12.7205C11.696 12.7294 11.1838 13.0516 10.9916 13.375C10.5994 14.0283 10.7949 14.666 10.7949 14.666Z"
|
||||
fill="#D92F0A"
|
||||
/>
|
||||
<path
|
||||
d="M8.97839 9.77071C8.97839 9.77071 8.96284 10.0618 9.0195 10.2829C9.13839 10.7495 9.54836 11.254 10.0817 11.7273C10.7694 12.3384 11.5683 12.7639 11.6271 12.7806C11.6871 12.7972 12.1116 12.815 12.1193 12.7717C12.1227 12.7572 12.1482 12.6939 12.0093 12.5928C11.7238 12.3872 11.0916 12.025 10.5739 11.6162C10.2083 11.3273 9.73947 10.9684 9.33171 10.2285C9.25727 10.094 9.28393 9.75405 9.28393 9.75405L8.97839 9.77071Z"
|
||||
fill="white"
|
||||
/>
|
||||
<path
|
||||
d="M3.32974 7.25306C3.32974 7.25306 2.44534 7.36861 2.41756 7.49416C2.38979 7.62082 2.48756 9.28962 2.48756 9.28962C2.48756 9.28962 2.85976 9.22074 3.04753 9.26185C3.2353 9.30296 3.58861 9.53183 3.58861 9.53183L4.18081 9.16741L4.60745 9.7596C4.60745 9.7596 4.90854 9.60405 5.32519 9.64516C5.74183 9.68627 6.02182 9.96736 6.02182 9.96736L6.60401 9.4885L7.25398 9.99959C7.25398 9.99959 7.55063 9.65405 8.10171 9.64405C8.65279 9.63405 8.98611 10.0396 8.98611 10.0396L9.71385 9.40517L10.4316 9.95625C10.4316 9.95625 10.7127 9.72738 11.0349 9.65516C11.3571 9.58183 11.596 9.70738 11.596 9.70738L12.0537 8.89631L12.5737 9.27074C12.5737 9.27074 12.7404 9.07297 12.9792 9.04186C13.2181 9.01075 13.5092 9.00075 13.5092 9.00075C13.5092 9.00075 13.4881 7.87748 13.4881 7.71082C13.4881 7.54416 13.4259 7.27417 13.3525 7.22195C13.2792 7.16973 12.6248 7.02419 12.6248 7.02419L3.32974 7.25306Z"
|
||||
fill="#D92F0A"
|
||||
/>
|
||||
<path
|
||||
d="M4.4997 7.73859L3.63197 7.93969L3.58752 9.53294C3.58752 9.53294 3.85195 9.45628 4.11639 9.48405C4.38082 9.51183 4.60636 9.76182 4.60636 9.76182C4.60636 9.76182 4.6008 8.72632 4.60969 8.55188C4.61858 8.37745 4.68636 8.04635 4.72302 7.98302C4.75968 7.91858 4.4997 7.73859 4.4997 7.73859Z"
|
||||
fill="#E1E1E1"
|
||||
/>
|
||||
<path
|
||||
d="M6.78407 8.07747L5.99189 8.36745C5.99189 8.36745 5.97078 9.10074 5.98967 9.39295C6.00744 9.68516 6.02078 9.96959 6.02078 9.96959C6.02078 9.96959 6.38187 9.84959 6.67519 9.86737C6.96739 9.88515 7.25293 10.0018 7.25293 10.0018L7.24182 8.433L6.78407 8.07747Z"
|
||||
fill="#E1E1E1"
|
||||
/>
|
||||
<path
|
||||
d="M8.95947 8.40634C8.95947 8.40634 8.98725 9.00075 8.99614 9.30184C9.00503 9.60294 8.98503 10.0429 8.98503 10.0429C8.98503 10.0429 9.41612 9.82293 9.75499 9.79515C10.0927 9.76738 10.4305 9.95959 10.4305 9.95959C10.4305 9.95959 10.4038 9.33851 10.3672 8.88187C10.3416 8.56299 10.2483 8.28745 10.2483 8.28745L9.38056 7.82192L8.95947 8.40634Z"
|
||||
fill="#E1E1E1"
|
||||
/>
|
||||
<path
|
||||
d="M11.5949 9.70961C11.5949 9.70961 11.8827 9.39296 12.1116 9.31963C12.3405 9.2463 12.5727 9.27296 12.5727 9.27296C12.5727 9.27296 12.5593 8.52522 12.5227 8.18635C12.486 7.84859 12.4527 7.65416 12.4527 7.65416L11.3905 7.19974L11.4205 8.03525C11.4205 8.03525 11.5461 8.31412 11.5827 8.79854C11.6183 9.28296 11.5949 9.70961 11.5949 9.70961Z"
|
||||
fill="#E1E1E1"
|
||||
/>
|
||||
<path
|
||||
d="M8.04625 4.2021L6.7641 4.77318C6.7641 4.77318 5.5986 5.64758 4.67643 6.23088C3.75425 6.81418 3.22428 7.03639 3.11429 7.08195C2.93874 7.15528 2.65431 7.24194 2.53099 7.32638C2.32655 7.46859 2.4121 7.53526 2.44543 7.54637C2.47877 7.55748 3.49871 8.01412 5.30639 8.28188C7.11408 8.54965 9.57172 8.44076 10.8317 8.19633C12.0905 7.9519 13.3526 7.22305 13.3526 7.22305C13.3526 7.22305 13.2471 7.04639 12.5482 6.74308C11.8483 6.43976 11.2539 6.11311 10.2395 5.4487C9.22508 4.78429 8.98065 4.45764 8.98065 4.45764L8.04625 4.2021Z"
|
||||
fill="#FF6110"
|
||||
/>
|
||||
<path
|
||||
d="M6.96185 5.00653C6.96185 5.00653 5.55081 6.452 3.81312 7.65305C3.62313 7.78416 3.63202 7.9397 3.63202 7.9397C3.63202 7.9397 3.77202 7.99303 4.11644 8.07303C4.46642 8.15414 4.66752 8.17414 4.66752 8.17414C4.66752 8.17414 4.8264 7.99192 5.16528 7.65305C5.77191 7.04642 6.14856 6.56533 6.62298 5.95092C6.9374 5.54316 7.31183 5.0543 7.31183 5.0543L6.96185 5.00653Z"
|
||||
fill="white"
|
||||
/>
|
||||
<path
|
||||
d="M7.5684 5.19318C7.5684 5.19318 7.0551 6.382 6.81067 6.89531C6.69512 7.13641 6.43735 7.58305 6.20403 7.95636C6.00071 8.2819 5.99182 8.36856 5.99182 8.36856C5.99182 8.36856 6.44846 8.43412 6.66956 8.43412C6.89066 8.43412 7.24064 8.43189 7.24064 8.43189C7.24064 8.43189 7.41619 7.90859 7.60285 6.85975C7.71173 6.24979 7.87061 5.18095 7.87061 5.18095L7.5684 5.19318Z"
|
||||
fill="white"
|
||||
/>
|
||||
<path
|
||||
d="M8.27954 5.23983C8.27954 5.23983 8.39509 6.38532 8.52397 6.98862C8.69841 7.80525 8.95839 8.40522 8.95839 8.40522C8.95839 8.40522 9.39837 8.38744 9.6428 8.36411C9.88723 8.34077 10.2461 8.28633 10.2461 8.28633C10.2461 8.28633 9.82613 7.6397 9.42059 6.98862C9.15283 6.55754 8.65063 5.55425 8.56953 5.16983C8.50064 4.83874 8.27954 5.23983 8.27954 5.23983Z"
|
||||
fill="white"
|
||||
/>
|
||||
<path
|
||||
d="M8.90845 5.02984C8.90845 5.06428 9.27732 5.85091 10.1906 6.83752C10.925 7.63081 11.415 8.03857 11.415 8.03857C11.415 8.03857 11.8338 7.90969 12.0216 7.8408C12.2427 7.75969 12.4527 7.65414 12.4527 7.65414C12.4527 7.65414 11.4616 6.9664 10.8783 6.48865C10.295 6.0109 9.49064 5.34649 9.26954 5.04317C9.04844 4.73874 8.90845 5.02984 8.90845 5.02984Z"
|
||||
fill="white"
|
||||
/>
|
||||
<path
|
||||
d="M8.03411 3.40993C7.78856 3.4277 7.70746 3.84102 7.57969 3.9699C7.45191 4.09767 6.76306 4.7743 6.76306 4.7743C6.76306 4.7743 6.88528 5.41316 8.09855 5.40093C9.20849 5.38871 9.41959 4.86318 9.41959 4.86318C9.41959 4.86318 8.71074 4.10989 8.54741 3.92323C8.38409 3.73547 8.36075 3.3866 8.03411 3.40993Z"
|
||||
fill="#D92F0A"
|
||||
/>
|
||||
<path
|
||||
d="M8.07516 2.86996C8.07516 2.86996 8.28515 2.8033 8.58402 2.83774C8.88845 2.8733 9.09066 2.97218 9.40731 2.96551C9.89173 2.95663 10.2817 2.72886 10.4217 2.59998C10.5617 2.4711 10.7406 2.26222 10.6472 2.19222C10.5539 2.12223 10.1562 2.22778 9.71174 2.05223C9.31954 1.89779 9.12288 1.49448 8.56624 1.43004C8.08738 1.3756 7.94739 1.57225 7.94739 1.57225L8.07516 2.86996Z"
|
||||
fill="#FF6110"
|
||||
/>
|
||||
<path
|
||||
d="M7.88843 1.43226V3.6177L8.23841 3.52437L8.20397 1.43226H7.88843Z"
|
||||
fill="#D92F0A"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export default TentIcon;
|
@ -0,0 +1,39 @@
|
||||
.time-selection-target {
|
||||
display: flex;
|
||||
height: 32px;
|
||||
padding: 6px 6px 6px 8px;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
align-self: stretch;
|
||||
border-radius: 2px;
|
||||
border: 1px solid var(--bg-slate-400);
|
||||
background: var(--bg-ink-300);
|
||||
box-shadow: none;
|
||||
|
||||
.button-selected-text {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.selected-value {
|
||||
color: var(--bg-vanilla-100);
|
||||
font-family: Inter;
|
||||
font-size: 12px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 16px; /* 133.333% */
|
||||
}
|
||||
}
|
||||
|
||||
.lightMode {
|
||||
.time-selection-target {
|
||||
border: 1px solid var(--bg-vanilla-300);
|
||||
background: var(--bg-vanilla-300);
|
||||
|
||||
.selected-value {
|
||||
color: var(--bg-ink-300);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,13 +1,15 @@
|
||||
import './TimePreference.styles.scss';
|
||||
|
||||
import { DownOutlined } from '@ant-design/icons';
|
||||
import { Button, Dropdown } from 'antd';
|
||||
import { Button, Dropdown, Typography } from 'antd';
|
||||
import TimeItems, {
|
||||
timePreferance,
|
||||
timePreferenceType,
|
||||
} from 'container/NewWidget/RightContainer/timeItems';
|
||||
import { Globe } from 'lucide-react';
|
||||
import { Dispatch, SetStateAction, useCallback, useMemo } from 'react';
|
||||
|
||||
import { menuItems } from './config';
|
||||
import { TextContainer } from './styles';
|
||||
|
||||
function TimePreference({
|
||||
setSelectedTime,
|
||||
@ -32,13 +34,22 @@ function TimePreference({
|
||||
);
|
||||
|
||||
return (
|
||||
<TextContainer noButtonMargin>
|
||||
<Dropdown menu={menu}>
|
||||
<Button>
|
||||
{selectedTime.name} <DownOutlined />
|
||||
</Button>
|
||||
</Dropdown>
|
||||
</TextContainer>
|
||||
<Dropdown
|
||||
menu={menu}
|
||||
rootClassName="time-selection-menu"
|
||||
className="time-selection-target"
|
||||
trigger={['click']}
|
||||
>
|
||||
<Button>
|
||||
<div className="button-selected-text">
|
||||
<Globe size={14} />
|
||||
<Typography.Text className="selected-value">
|
||||
{selectedTime.name}
|
||||
</Typography.Text>
|
||||
</div>
|
||||
<DownOutlined />
|
||||
</Button>
|
||||
</Dropdown>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -267,6 +267,21 @@ function AppLayout(props: AppLayoutProps): JSX.Element {
|
||||
const isTracesView = (): boolean =>
|
||||
routeKey === 'TRACES_EXPLORER' || routeKey === 'TRACES_SAVE_VIEWS';
|
||||
|
||||
const isDashboardListView = (): boolean => routeKey === 'ALL_DASHBOARD';
|
||||
const isDashboardView = (): boolean => {
|
||||
/**
|
||||
* need to match using regex here as the getRoute function will not work for
|
||||
* routes with id
|
||||
*/
|
||||
const regex = /^\/dashboard\/[a-zA-Z0-9_-]+$/;
|
||||
return regex.test(pathname);
|
||||
};
|
||||
|
||||
const isDashboardWidgetView = (): boolean => {
|
||||
const regex = /^\/dashboard\/[a-zA-Z0-9_-]+\/new$/;
|
||||
return regex.test(pathname);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (isDarkMode) {
|
||||
document.body.classList.remove('lightMode');
|
||||
@ -331,7 +346,14 @@ function AppLayout(props: AppLayoutProps): JSX.Element {
|
||||
<LayoutContent>
|
||||
<ChildrenContainer
|
||||
style={{
|
||||
margin: isLogsView() || isTracesView() ? 0 : ' 0 1rem',
|
||||
margin:
|
||||
isLogsView() ||
|
||||
isTracesView() ||
|
||||
isDashboardView() ||
|
||||
isDashboardWidgetView() ||
|
||||
isDashboardListView()
|
||||
? 0
|
||||
: '0 1rem',
|
||||
}}
|
||||
>
|
||||
{isToDisplayLayout && !renderFullScreen && <TopNav />}
|
||||
|
@ -15,6 +15,9 @@ export const Layout = styled(LayoutComponent)`
|
||||
export const LayoutContent = styled(LayoutComponent.Content)`
|
||||
overflow-y: auto;
|
||||
height: 100%;
|
||||
&::-webkit-scrollbar {
|
||||
width: 0.1rem;
|
||||
}
|
||||
`;
|
||||
|
||||
export const ChildrenContainer = styled.div`
|
||||
|
@ -22,6 +22,18 @@ export const ChartContainer = styled(Card)`
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.plot-tag {
|
||||
margin-left: 6px;
|
||||
display: inline-flex;
|
||||
padding: 0px 4px 0px 6px;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
border-radius: 4px;
|
||||
background: var(--bg-slate-400);
|
||||
backdrop-filter: blur(6px);
|
||||
width: fit-content;
|
||||
}
|
||||
|
||||
.ant-card-body {
|
||||
padding: 1.5rem 0;
|
||||
height: 57vh;
|
||||
|
@ -0,0 +1,244 @@
|
||||
.dashboard-empty-state {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 500px;
|
||||
|
||||
.dashboard-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 12px;
|
||||
|
||||
.heading {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
|
||||
.icons {
|
||||
color: white;
|
||||
}
|
||||
|
||||
.welcome {
|
||||
color: var(--bg-vanilla-400);
|
||||
font-family: Inter;
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 24px; /* 171.429% */
|
||||
letter-spacing: -0.07px;
|
||||
}
|
||||
|
||||
.welcome-info {
|
||||
color: var(--bg-vanilla-100);
|
||||
font-family: Inter;
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
line-height: 24px;
|
||||
letter-spacing: -0.07px;
|
||||
}
|
||||
}
|
||||
|
||||
.actions {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.actions-1 {
|
||||
display: flex;
|
||||
width: 560px;
|
||||
padding: 12px;
|
||||
justify-content: space-between;
|
||||
align-items: flex-start;
|
||||
border-radius: 4px 4px 0px 0px;
|
||||
border: 1px solid var(--bg-slate-500);
|
||||
background: var(--bg-ink-400);
|
||||
|
||||
.configure-button {
|
||||
display: flex;
|
||||
width: 113px;
|
||||
height: 32px;
|
||||
padding: 6px;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex-shrink: 0;
|
||||
border-radius: 2px;
|
||||
border: 1px solid var(--bg-slate-400);
|
||||
background: var(--bg-ink-300);
|
||||
color: var(--bg-vanilla-400);
|
||||
font-family: Inter;
|
||||
font-size: 12px;
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
line-height: 10px; /* 83.333% */
|
||||
letter-spacing: 0.12px;
|
||||
}
|
||||
|
||||
.actions-configure {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 6px;
|
||||
|
||||
.actions-configure-text {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
align-items: center;
|
||||
|
||||
.icons {
|
||||
color: white;
|
||||
}
|
||||
|
||||
.configure {
|
||||
color: var(--bg-vanilla-100);
|
||||
font-family: Inter;
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 20px; /* 142.857% */
|
||||
letter-spacing: -0.07px;
|
||||
}
|
||||
}
|
||||
.configure-info {
|
||||
color: var(--bg-vanilla-400);
|
||||
font-family: Inter;
|
||||
font-size: 12px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 20px; /* 166.667% */
|
||||
letter-spacing: -0.06px;
|
||||
padding-left: 22px;
|
||||
}
|
||||
}
|
||||
|
||||
.add-panel-btn {
|
||||
display: flex;
|
||||
width: 113px;
|
||||
height: 32px;
|
||||
padding: 6px;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex-shrink: 0;
|
||||
border-radius: 2px;
|
||||
border: 1px solid var(--bg-slate-400);
|
||||
background: var(--bg-ink-300);
|
||||
box-shadow: none;
|
||||
color: var(--bg-vanilla-400);
|
||||
font-family: Inter;
|
||||
font-size: 12px;
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
line-height: 10px; /* 83.333% */
|
||||
letter-spacing: 0.12px;
|
||||
}
|
||||
|
||||
.actions-add-panel {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 6px;
|
||||
|
||||
.actions-panel-text {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
align-items: center;
|
||||
|
||||
.icons {
|
||||
color: white;
|
||||
}
|
||||
|
||||
.panel {
|
||||
color: var(--bg-vanilla-100);
|
||||
font-family: Inter;
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 20px; /* 142.857% */
|
||||
letter-spacing: -0.07px;
|
||||
}
|
||||
}
|
||||
|
||||
.panel-info {
|
||||
color: var(--bg-vanilla-400);
|
||||
font-family: Inter;
|
||||
font-size: 12px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 20px; /* 166.667% */
|
||||
letter-spacing: -0.06px;
|
||||
padding-left: 22px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.lightMode {
|
||||
.dashboard-empty-state {
|
||||
.dashboard-content {
|
||||
.heading {
|
||||
.icons {
|
||||
color: var(--bg-ink-300);
|
||||
}
|
||||
|
||||
.welcome {
|
||||
color: var(--bg-ink-400);
|
||||
}
|
||||
|
||||
.welcome-info {
|
||||
color: var(--bg-ink-400);
|
||||
}
|
||||
}
|
||||
|
||||
.actions {
|
||||
.actions-1 {
|
||||
border: 1px solid var(--bg-vanilla-300);
|
||||
background: var(--bg-vanilla-100);
|
||||
|
||||
.configure-button {
|
||||
border: 1px solid var(--bg-vanilla-300);
|
||||
background: var(--bg-vanilla-300);
|
||||
color: var(--bg-ink-400);
|
||||
}
|
||||
|
||||
.actions-configure {
|
||||
.actions-configure-text {
|
||||
.icons {
|
||||
color: var(--bg-ink-400);
|
||||
}
|
||||
|
||||
.configure {
|
||||
color: var(--bg-ink-400);
|
||||
}
|
||||
}
|
||||
.configure-info {
|
||||
color: var(--bg-ink-400);
|
||||
}
|
||||
}
|
||||
|
||||
.add-panel-btn {
|
||||
border: 1px solid var(--bg-vanilla-300);
|
||||
background: var(--bg-vanilla-300);
|
||||
color: var(--bg-ink-400);
|
||||
}
|
||||
|
||||
.actions-add-panel {
|
||||
.actions-panel-text {
|
||||
.icons {
|
||||
color: var(--bg-ink-300);
|
||||
}
|
||||
|
||||
.panel {
|
||||
color: var(--bg-ink-300);
|
||||
}
|
||||
}
|
||||
|
||||
.panel-info {
|
||||
color: var(--bg-ink-300);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,105 @@
|
||||
/* eslint-disable jsx-a11y/img-redundant-alt */
|
||||
import './DashboardEmptyState.styles.scss';
|
||||
|
||||
import { PlusOutlined } from '@ant-design/icons';
|
||||
import { Button, Typography } from 'antd';
|
||||
import SettingsDrawer from 'container/NewDashboard/DashboardDescription/SettingsDrawer';
|
||||
import useComponentPermission from 'hooks/useComponentPermission';
|
||||
import { useDashboard } from 'providers/Dashboard/Dashboard';
|
||||
import { useCallback } from 'react';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { AppState } from 'store/reducers';
|
||||
import AppReducer from 'types/reducer/app';
|
||||
import { ROLES, USER_ROLES } from 'types/roles';
|
||||
import { ComponentTypes } from 'utils/permission';
|
||||
|
||||
export default function DashboardEmptyState(): JSX.Element {
|
||||
const {
|
||||
selectedDashboard,
|
||||
isDashboardLocked,
|
||||
handleToggleDashboardSlider,
|
||||
} = useDashboard();
|
||||
|
||||
const { user, role } = useSelector<AppState, AppReducer>((state) => state.app);
|
||||
let permissions: ComponentTypes[] = ['add_panel'];
|
||||
|
||||
if (isDashboardLocked) {
|
||||
permissions = ['add_panel_locked_dashboard'];
|
||||
}
|
||||
|
||||
const userRole: ROLES | null =
|
||||
selectedDashboard?.created_by === user?.email
|
||||
? (USER_ROLES.AUTHOR as ROLES)
|
||||
: role;
|
||||
|
||||
const [addPanelPermission] = useComponentPermission(permissions, userRole);
|
||||
|
||||
const onEmptyWidgetHandler = useCallback(() => {
|
||||
handleToggleDashboardSlider(true);
|
||||
}, [handleToggleDashboardSlider]);
|
||||
return (
|
||||
<section className="dashboard-empty-state">
|
||||
<div className="dashboard-content">
|
||||
<section className="heading">
|
||||
<img
|
||||
src="/Icons/dashboard_emoji.svg"
|
||||
alt="header-image"
|
||||
style={{ height: '32px', width: '32px' }}
|
||||
/>
|
||||
<Typography.Text className="welcome">
|
||||
Welcome to your new dashboard
|
||||
</Typography.Text>
|
||||
<Typography.Text className="welcome-info">
|
||||
Follow the steps to populate it with data and share with your teammates
|
||||
</Typography.Text>
|
||||
</section>
|
||||
<section className="actions">
|
||||
<div className="actions-1">
|
||||
<div className="actions-configure">
|
||||
<div className="actions-configure-text">
|
||||
<img
|
||||
src="/Icons/tools.svg"
|
||||
alt="header-image"
|
||||
style={{ height: '14px', width: '14px' }}
|
||||
/>
|
||||
<Typography.Text className="configure">
|
||||
Configure your new dashboard
|
||||
</Typography.Text>
|
||||
</div>
|
||||
<Typography.Text className="configure-info">
|
||||
Give it a name, add description, tags and variables
|
||||
</Typography.Text>
|
||||
</div>
|
||||
<SettingsDrawer drawerTitle="Dashboard Configuration" />
|
||||
</div>
|
||||
<div className="actions-1">
|
||||
<div className="actions-add-panel">
|
||||
<div className="actions-panel-text">
|
||||
<img
|
||||
src="/Icons/landscape.svg"
|
||||
alt="header-image"
|
||||
style={{ height: '14px', width: '14px' }}
|
||||
/>
|
||||
<Typography.Text className="panel">Add panels</Typography.Text>
|
||||
</div>
|
||||
<Typography.Text className="panel-info">
|
||||
Add panels to visualize your data
|
||||
</Typography.Text>
|
||||
</div>
|
||||
{!isDashboardLocked && addPanelPermission && (
|
||||
<Button
|
||||
className="add-panel-btn"
|
||||
onClick={onEmptyWidgetHandler}
|
||||
icon={<PlusOutlined />}
|
||||
type="primary"
|
||||
data-testid="add-panel"
|
||||
>
|
||||
New Panel
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
@ -6,12 +6,85 @@
|
||||
border: none !important;
|
||||
margin-top: 0;
|
||||
|
||||
.row-panel {
|
||||
border-radius: 4px;
|
||||
background: rgba(18, 19, 23, 0.4);
|
||||
padding: 8px;
|
||||
display: flex;
|
||||
gap: 6px;
|
||||
align-items: center;
|
||||
|
||||
.settings-icon {
|
||||
color: var(--bg-vanilla-400);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.row-icon {
|
||||
color: var(--bg-vanilla-400);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.grip {
|
||||
cursor: move;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
color: var(--bg-vanilla-400);
|
||||
font-family: Inter;
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 20px; /* 142.857% */
|
||||
letter-spacing: -0.07px;
|
||||
}
|
||||
}
|
||||
|
||||
.widget-graph-container {
|
||||
&.graph {
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.footer {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
width: -webkit-fill-available;
|
||||
|
||||
.locked-text {
|
||||
align-self: flex-end;
|
||||
width: 80px;
|
||||
border: none;
|
||||
cursor: default;
|
||||
display: inline-flex;
|
||||
padding: 4px 6px;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
border-radius: 4px 0px 0px 0px;
|
||||
background: var(--bg-sakura-500);
|
||||
backdrop-filter: blur(6px);
|
||||
color: var(--bg-ink-500);
|
||||
font-family: 'Space Mono';
|
||||
font-size: 12px;
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
line-height: 16px; /* 133.333% */
|
||||
letter-spacing: 0.48px;
|
||||
text-transform: uppercase;
|
||||
|
||||
.ant-btn-icon {
|
||||
margin-inline-end: 0px;
|
||||
}
|
||||
}
|
||||
|
||||
.locked-bar {
|
||||
background: var(--bg-sakura-500);
|
||||
height: 6px;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.widget-graph-container {
|
||||
@ -32,18 +105,257 @@
|
||||
}
|
||||
}
|
||||
|
||||
.row-settings {
|
||||
.ant-popover-inner {
|
||||
width: 191px;
|
||||
flex-shrink: 0;
|
||||
border-radius: 4px;
|
||||
border: 1px solid var(--bg-slate-400);
|
||||
background: linear-gradient(
|
||||
139deg,
|
||||
rgba(18, 19, 23, 0.8) 0%,
|
||||
rgba(18, 19, 23, 0.9) 98.68%
|
||||
);
|
||||
box-shadow: 4px 10px 16px 2px rgba(0, 0, 0, 0.2);
|
||||
backdrop-filter: blur(20px);
|
||||
padding: 0px;
|
||||
|
||||
.menu-content {
|
||||
.section-1 {
|
||||
.rename-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
color: var(--bg-vanilla-400);
|
||||
font-family: Inter;
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: normal;
|
||||
letter-spacing: 0.14px;
|
||||
padding: 14px;
|
||||
width: 100%;
|
||||
.ant-btn-icon {
|
||||
margin-inline-end: 0px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.section-2 {
|
||||
border-top: 1px solid #1d212d;
|
||||
.remove-section {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
gap: 6px;
|
||||
color: var(--bg-vanilla-400);
|
||||
font-family: Inter;
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: normal;
|
||||
letter-spacing: 0.14px;
|
||||
padding: 10px 18px 12px 14px;
|
||||
color: var(--bg-cherry-400);
|
||||
font-family: Inter;
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: normal;
|
||||
letter-spacing: 0.14px;
|
||||
.ant-btn-icon {
|
||||
margin-inline-end: 0px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.rename-section {
|
||||
.ant-modal-content {
|
||||
width: 384px;
|
||||
height: auto;
|
||||
flex-shrink: 0;
|
||||
border-radius: 4px;
|
||||
border: 1px solid var(--bg-slate-500);
|
||||
background: var(--Ink-400, #121317);
|
||||
box-shadow: 0px -4px 16px 2px rgba(0, 0, 0, 0.2);
|
||||
padding: 0px;
|
||||
|
||||
.ant-modal-header {
|
||||
padding: 16px;
|
||||
background: var(--bg-ink-400);
|
||||
border-bottom: 1px solid var(--bg-slate-500);
|
||||
margin-bottom: 0px;
|
||||
|
||||
.ant-modal-title {
|
||||
color: var(--bg-vanilla-100);
|
||||
font-family: Inter;
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 20px; /* 142.857% */
|
||||
}
|
||||
}
|
||||
|
||||
.ant-modal-body {
|
||||
padding: 12px 16px 16px 16px;
|
||||
|
||||
.typography {
|
||||
color: var(--bg-vanilla-100);
|
||||
font-family: Inter;
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
line-height: 20px; /* 142.857% */
|
||||
}
|
||||
|
||||
.ant-form-item {
|
||||
margin-bottom: 0px;
|
||||
|
||||
.ant-input {
|
||||
margin-top: 8px;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.action-btns {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-direction: row-reverse;
|
||||
gap: 12px;
|
||||
|
||||
.ok-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
color: var(--bg-vanilla-100);
|
||||
font-family: Inter;
|
||||
font-size: 12px;
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
line-height: 24px; /* 200% */
|
||||
display: flex;
|
||||
width: 140px;
|
||||
padding: 4px 8px;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
border-radius: 2px;
|
||||
background: var(--bg-robin-500);
|
||||
|
||||
.ant-btn-icon {
|
||||
margin-inline-end: 0px;
|
||||
}
|
||||
}
|
||||
|
||||
.cancel-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
color: var(--bg-vanilla-400);
|
||||
font-family: Inter;
|
||||
font-size: 12px;
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
line-height: 24px; /* 200% */
|
||||
display: flex;
|
||||
padding: 4px 8px;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
border-radius: 2px;
|
||||
background: var(--bg-slate-500);
|
||||
|
||||
.ant-btn-icon {
|
||||
margin-inline-end: 0px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.lightMode {
|
||||
.fullscreen-grid-container {
|
||||
background-color: rgb(250, 250, 250);
|
||||
.react-grid-layout {
|
||||
.row-panel {
|
||||
background: var(--bg-vanilla-200);
|
||||
|
||||
.settings-icon {
|
||||
color: var(--bg-ink-400);
|
||||
}
|
||||
|
||||
.row-icon {
|
||||
color: var(--bg-ink-400);
|
||||
}
|
||||
|
||||
.section-title {
|
||||
color: var(--bg-ink-400);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.widget-full-view {
|
||||
.ant-modal-content {
|
||||
background-color: var(--bg-vanilla-100);
|
||||
}
|
||||
|
||||
.ant-modal-header {
|
||||
background-color: var(--bg-vanilla-100);
|
||||
.ant-modal-header {
|
||||
background-color: var(--bg-vanilla-100);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.row-settings {
|
||||
.ant-popover-inner {
|
||||
border: 1px solid var(--bg-vanilla-300);
|
||||
background: var(--bg-vanilla-100);
|
||||
|
||||
.menu-content {
|
||||
.section-1 {
|
||||
.rename-btn {
|
||||
color: var(--bg-ink-400);
|
||||
}
|
||||
}
|
||||
|
||||
.section-2 {
|
||||
border-top: 1px solid var(--bg-vanilla-300);
|
||||
.remove-section {
|
||||
color: var(--bg-cherry-400);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.rename-section {
|
||||
.ant-modal-content {
|
||||
border: 1px solid var(--bg-vanilla-300);
|
||||
background: var(--bg-vanilla-100);
|
||||
|
||||
.ant-modal-header {
|
||||
background: var(--bg-vanilla-100);
|
||||
border-bottom: 1px solid var(--bg-vanilla-300);
|
||||
|
||||
.ant-modal-title {
|
||||
color: var(--bg-ink-300);
|
||||
}
|
||||
}
|
||||
|
||||
.ant-modal-body {
|
||||
.typography {
|
||||
color: var(--bg-ink-100);
|
||||
}
|
||||
|
||||
.ant-form-item {
|
||||
.action-btns {
|
||||
.cancel-btn {
|
||||
color: var(--bg-ink-300);
|
||||
background: var(--bg-vanilla-300);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,15 +1,14 @@
|
||||
import './GridCardLayout.styles.scss';
|
||||
|
||||
import { PlusOutlined } from '@ant-design/icons';
|
||||
import { Flex, Form, Input, Modal, Tooltip, Typography } from 'antd';
|
||||
import { Color } from '@signozhq/design-tokens';
|
||||
import { Button, Form, Input, Modal, Typography } from 'antd';
|
||||
import { useForm } from 'antd/es/form/Form';
|
||||
import cx from 'classnames';
|
||||
import FacingIssueBtn from 'components/facingIssueBtn/FacingIssueBtn';
|
||||
import { dashboardHelpMessage } from 'components/facingIssueBtn/util';
|
||||
import { SOMETHING_WENT_WRONG } from 'constants/api';
|
||||
import { QueryParams } from 'constants/query';
|
||||
import { PANEL_GROUP_TYPES, PANEL_TYPES } from 'constants/queryBuilder';
|
||||
import { themeColors } from 'constants/theme';
|
||||
import { DEFAULT_ROW_NAME } from 'container/NewDashboard/DashboardDescription/utils';
|
||||
import { useUpdateDashboard } from 'hooks/dashboard/useUpdateDashboard';
|
||||
import useComponentPermission from 'hooks/useComponentPermission';
|
||||
import { useIsDarkMode } from 'hooks/useDarkMode';
|
||||
@ -19,19 +18,18 @@ import history from 'lib/history';
|
||||
import { defaultTo } from 'lodash-es';
|
||||
import isEqual from 'lodash-es/isEqual';
|
||||
import {
|
||||
FullscreenIcon,
|
||||
Check,
|
||||
ChevronDown,
|
||||
ChevronUp,
|
||||
GripVertical,
|
||||
MoveDown,
|
||||
MoveUp,
|
||||
Settings,
|
||||
Trash2,
|
||||
LockKeyhole,
|
||||
X,
|
||||
} from 'lucide-react';
|
||||
import { useDashboard } from 'providers/Dashboard/Dashboard';
|
||||
import { sortLayout } from 'providers/Dashboard/util';
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
import { FullScreen, useFullScreenHandle } from 'react-full-screen';
|
||||
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { FullScreen, FullScreenHandle } from 'react-full-screen';
|
||||
import { ItemCallback, Layout } from 'react-grid-layout';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
import { UpdateTimeInterval } from 'store/actions';
|
||||
@ -40,21 +38,21 @@ import { Dashboard, Widgets } from 'types/api/dashboard/getAll';
|
||||
import AppReducer from 'types/reducer/app';
|
||||
import { ROLES, USER_ROLES } from 'types/roles';
|
||||
import { ComponentTypes } from 'utils/permission';
|
||||
import { v4 as uuid } from 'uuid';
|
||||
|
||||
import { EditMenuAction, ViewMenuAction } from './config';
|
||||
import DashboardEmptyState from './DashboardEmptyState/DashboardEmptyState';
|
||||
import GridCard from './GridCard';
|
||||
import {
|
||||
Button,
|
||||
ButtonContainer,
|
||||
Card,
|
||||
CardContainer,
|
||||
ReactGridLayout,
|
||||
} from './styles';
|
||||
import { GraphLayoutProps } from './types';
|
||||
import { Card, CardContainer, ReactGridLayout } from './styles';
|
||||
import { removeUndefinedValuesFromLayout } from './utils';
|
||||
import { WidgetRowHeader } from './WidgetRow';
|
||||
|
||||
function GraphLayout({ onAddPanelHandler }: GraphLayoutProps): JSX.Element {
|
||||
interface GraphLayoutProps {
|
||||
handle: FullScreenHandle;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line sonarjs/cognitive-complexity
|
||||
function GraphLayout(props: GraphLayoutProps): JSX.Element {
|
||||
const { handle } = props;
|
||||
const {
|
||||
selectedDashboard,
|
||||
layouts,
|
||||
@ -65,14 +63,11 @@ function GraphLayout({ onAddPanelHandler }: GraphLayoutProps): JSX.Element {
|
||||
isDashboardLocked,
|
||||
} = useDashboard();
|
||||
const { data } = selectedDashboard || {};
|
||||
const handle = useFullScreenHandle();
|
||||
const { pathname } = useLocation();
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const { widgets, variables } = data || {};
|
||||
|
||||
const { t } = useTranslation(['dashboard']);
|
||||
|
||||
const { featureResponse, role, user } = useSelector<AppState, AppReducer>(
|
||||
(state) => state.app,
|
||||
);
|
||||
@ -122,6 +117,11 @@ function GraphLayout({ onAddPanelHandler }: GraphLayoutProps): JSX.Element {
|
||||
userRole,
|
||||
);
|
||||
|
||||
const [deleteWidget, editWidget] = useComponentPermission(
|
||||
['delete_widget', 'edit_widget'],
|
||||
role,
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
setDashboardLayout(sortLayout(layouts));
|
||||
}, [layouts]);
|
||||
@ -206,80 +206,6 @@ function GraphLayout({ onAddPanelHandler }: GraphLayoutProps): JSX.Element {
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [dashboardLayout]);
|
||||
|
||||
function handleAddRow(): void {
|
||||
if (!selectedDashboard) return;
|
||||
const id = uuid();
|
||||
|
||||
const newRowWidgetMap: { widgets: Layout[]; collapsed: boolean } = {
|
||||
widgets: [],
|
||||
collapsed: false,
|
||||
};
|
||||
const currentRowIdx = 0;
|
||||
for (let j = currentRowIdx; j < dashboardLayout.length; j++) {
|
||||
if (!currentPanelMap[dashboardLayout[j].i]) {
|
||||
newRowWidgetMap.widgets.push(dashboardLayout[j]);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const updatedDashboard: Dashboard = {
|
||||
...selectedDashboard,
|
||||
data: {
|
||||
...selectedDashboard.data,
|
||||
layout: [
|
||||
{
|
||||
i: id,
|
||||
w: 12,
|
||||
minW: 12,
|
||||
minH: 1,
|
||||
maxH: 1,
|
||||
x: 0,
|
||||
h: 1,
|
||||
y: 0,
|
||||
},
|
||||
...dashboardLayout.filter((e) => e.i !== PANEL_TYPES.EMPTY_WIDGET),
|
||||
],
|
||||
panelMap: { ...currentPanelMap, [id]: newRowWidgetMap },
|
||||
widgets: [
|
||||
...(selectedDashboard.data.widgets || []),
|
||||
{
|
||||
id,
|
||||
title: 'Sample Row',
|
||||
description: '',
|
||||
panelTypes: PANEL_GROUP_TYPES.ROW,
|
||||
},
|
||||
],
|
||||
},
|
||||
uuid: selectedDashboard.uuid,
|
||||
};
|
||||
|
||||
updateDashboardMutation.mutate(updatedDashboard, {
|
||||
// eslint-disable-next-line sonarjs/no-identical-functions
|
||||
onSuccess: (updatedDashboard) => {
|
||||
if (updatedDashboard.payload) {
|
||||
if (updatedDashboard.payload.data.layout)
|
||||
setLayouts(sortLayout(updatedDashboard.payload.data.layout));
|
||||
setSelectedDashboard(updatedDashboard.payload);
|
||||
setPanelMap(updatedDashboard.payload?.data?.panelMap || {});
|
||||
}
|
||||
|
||||
featureResponse.refetch();
|
||||
},
|
||||
// eslint-disable-next-line sonarjs/no-identical-functions
|
||||
onError: () => {
|
||||
notifications.error({
|
||||
message: SOMETHING_WENT_WRONG,
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
const handleRowSettingsClick = (id: string): void => {
|
||||
setIsSettingsModalOpen(true);
|
||||
setCurrentSelectRowId(id);
|
||||
};
|
||||
|
||||
const onSettingsModalSubmit = (): void => {
|
||||
const newTitle = form.getFieldValue('title');
|
||||
if (!selectedDashboard) return;
|
||||
@ -330,6 +256,15 @@ function GraphLayout({ onAddPanelHandler }: GraphLayoutProps): JSX.Element {
|
||||
});
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (!currentSelectRowId) return;
|
||||
form.setFieldValue(
|
||||
'title',
|
||||
(widgets?.find((widget) => widget.id === currentSelectRowId)
|
||||
?.title as string) || DEFAULT_ROW_NAME,
|
||||
);
|
||||
}, [currentSelectRowId, form, widgets]);
|
||||
|
||||
// eslint-disable-next-line sonarjs/cognitive-complexity
|
||||
const handleRowCollapse = (id: string): void => {
|
||||
if (!selectedDashboard) return;
|
||||
@ -483,192 +418,187 @@ function GraphLayout({ onAddPanelHandler }: GraphLayoutProps): JSX.Element {
|
||||
},
|
||||
});
|
||||
};
|
||||
return (
|
||||
<>
|
||||
<Flex justify="flex-end" gap={8} align="center">
|
||||
<FacingIssueBtn
|
||||
attributes={{
|
||||
uuid: selectedDashboard?.uuid,
|
||||
title: data?.title,
|
||||
screen: 'Dashboard Details',
|
||||
}}
|
||||
eventName="Dashboard: Facing Issues in dashboard"
|
||||
buttonText="Need help with this dashboard?"
|
||||
message={dashboardHelpMessage(data, selectedDashboard)}
|
||||
onHoverText="Click here to get help for this dashboard"
|
||||
/>
|
||||
<ButtonContainer>
|
||||
<Tooltip title="Open in Full Screen">
|
||||
<Button
|
||||
className="periscope-btn"
|
||||
loading={updateDashboardMutation.isLoading}
|
||||
onClick={handle.enter}
|
||||
icon={<FullscreenIcon size={16} />}
|
||||
disabled={updateDashboardMutation.isLoading}
|
||||
/>
|
||||
</Tooltip>
|
||||
|
||||
{!isDashboardLocked && addPanelPermission && (
|
||||
<Button
|
||||
className="periscope-btn"
|
||||
onClick={onAddPanelHandler}
|
||||
icon={<PlusOutlined />}
|
||||
data-testid="add-panel"
|
||||
>
|
||||
{t('dashboard:add_panel')}
|
||||
</Button>
|
||||
)}
|
||||
{!isDashboardLocked && addPanelPermission && (
|
||||
<Button
|
||||
className="periscope-btn"
|
||||
onClick={(): void => handleAddRow()}
|
||||
icon={<PlusOutlined />}
|
||||
data-testid="add-row"
|
||||
>
|
||||
{t('dashboard:add_row')}
|
||||
</Button>
|
||||
)}
|
||||
</ButtonContainer>
|
||||
</Flex>
|
||||
|
||||
<FullScreen handle={handle} className="fullscreen-grid-container">
|
||||
<ReactGridLayout
|
||||
cols={12}
|
||||
rowHeight={45}
|
||||
autoSize
|
||||
width={100}
|
||||
useCSSTransforms
|
||||
isDraggable={!isDashboardLocked && addPanelPermission}
|
||||
isDroppable={!isDashboardLocked && addPanelPermission}
|
||||
isResizable={!isDashboardLocked && addPanelPermission}
|
||||
allowOverlap={false}
|
||||
onLayoutChange={handleLayoutChange}
|
||||
onDragStop={handleDragStop}
|
||||
draggableHandle=".drag-handle"
|
||||
layout={dashboardLayout}
|
||||
style={{ backgroundColor: isDarkMode ? '' : themeColors.snowWhite }}
|
||||
>
|
||||
{dashboardLayout.map((layout) => {
|
||||
const { i: id } = layout;
|
||||
const currentWidget = (widgets || [])?.find((e) => e.id === id);
|
||||
|
||||
if (currentWidget?.panelTypes === PANEL_GROUP_TYPES.ROW) {
|
||||
const rowWidgetProperties = currentPanelMap[id] || {};
|
||||
return (
|
||||
<CardContainer
|
||||
className="row-card"
|
||||
isDarkMode={isDarkMode}
|
||||
key={id}
|
||||
data-grid={JSON.stringify(currentWidget)}
|
||||
>
|
||||
<div className={cx('row-panel')}>
|
||||
<div style={{ display: 'flex', gap: '10px', alignItems: 'center' }}>
|
||||
<Button
|
||||
disabled={updateDashboardMutation.isLoading}
|
||||
icon={
|
||||
rowWidgetProperties.collapsed ? (
|
||||
<MoveDown size={14} />
|
||||
) : (
|
||||
<MoveUp size={14} />
|
||||
)
|
||||
}
|
||||
type="text"
|
||||
onClick={(): void => handleRowCollapse(id)}
|
||||
/>
|
||||
<Typography.Text>{currentWidget.title}</Typography.Text>
|
||||
<Button
|
||||
icon={<Settings size={14} />}
|
||||
type="text"
|
||||
onClick={(): void => handleRowSettingsClick(id)}
|
||||
/>
|
||||
</div>
|
||||
{rowWidgetProperties.collapsed && (
|
||||
<Button
|
||||
type="text"
|
||||
icon={<GripVertical size={14} />}
|
||||
className="drag-handle"
|
||||
/>
|
||||
)}
|
||||
{!rowWidgetProperties.collapsed && (
|
||||
<Button
|
||||
type="text"
|
||||
icon={<Trash2 size={14} />}
|
||||
onClick={(): void => {
|
||||
setIsDeleteModalOpen(true);
|
||||
setCurrentSelectRowId(id);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</CardContainer>
|
||||
);
|
||||
}
|
||||
const isDashboardEmpty = useMemo(
|
||||
() =>
|
||||
selectedDashboard?.data.layout
|
||||
? selectedDashboard?.data.layout?.length === 0
|
||||
: true,
|
||||
[selectedDashboard],
|
||||
);
|
||||
return isDashboardEmpty ? (
|
||||
<DashboardEmptyState />
|
||||
) : (
|
||||
<FullScreen handle={handle} className="fullscreen-grid-container">
|
||||
<ReactGridLayout
|
||||
cols={12}
|
||||
rowHeight={45}
|
||||
autoSize
|
||||
width={100}
|
||||
useCSSTransforms
|
||||
isDraggable={!isDashboardLocked && addPanelPermission}
|
||||
isDroppable={!isDashboardLocked && addPanelPermission}
|
||||
isResizable={!isDashboardLocked && addPanelPermission}
|
||||
allowOverlap={false}
|
||||
onLayoutChange={handleLayoutChange}
|
||||
onDragStop={handleDragStop}
|
||||
draggableHandle=".drag-handle"
|
||||
layout={dashboardLayout}
|
||||
style={{ backgroundColor: isDarkMode ? '' : themeColors.snowWhite }}
|
||||
>
|
||||
{dashboardLayout.map((layout) => {
|
||||
const { i: id } = layout;
|
||||
const currentWidget = (widgets || [])?.find((e) => e.id === id);
|
||||
|
||||
if (currentWidget?.panelTypes === PANEL_GROUP_TYPES.ROW) {
|
||||
const rowWidgetProperties = currentPanelMap[id] || {};
|
||||
return (
|
||||
<CardContainer
|
||||
className={isDashboardLocked ? '' : 'enable-resize'}
|
||||
className="row-card"
|
||||
isDarkMode={isDarkMode}
|
||||
key={id}
|
||||
data-grid={JSON.stringify(currentWidget)}
|
||||
>
|
||||
<Card
|
||||
className="grid-item"
|
||||
$panelType={currentWidget?.panelTypes || PANEL_TYPES.TIME_SERIES}
|
||||
>
|
||||
<GridCard
|
||||
widget={(currentWidget as Widgets) || ({ id, query: {} } as Widgets)}
|
||||
headerMenuList={widgetActions}
|
||||
variables={variables}
|
||||
version={selectedDashboard?.data?.version}
|
||||
onDragSelect={onDragSelect}
|
||||
<div className={cx('row-panel')}>
|
||||
<div style={{ display: 'flex', gap: '6px', alignItems: 'center' }}>
|
||||
{rowWidgetProperties.collapsed && (
|
||||
<GripVertical
|
||||
size={14}
|
||||
className="drag-handle"
|
||||
color={isDarkMode ? Color.BG_VANILLA_100 : Color.BG_INK_300}
|
||||
cursor="move"
|
||||
/>
|
||||
)}
|
||||
<Typography.Text className="section-title">
|
||||
{currentWidget.title}
|
||||
</Typography.Text>
|
||||
{rowWidgetProperties.collapsed ? (
|
||||
<ChevronDown
|
||||
size={14}
|
||||
onClick={(): void => handleRowCollapse(id)}
|
||||
className="row-icon"
|
||||
/>
|
||||
) : (
|
||||
<ChevronUp
|
||||
size={14}
|
||||
onClick={(): void => handleRowCollapse(id)}
|
||||
className="row-icon"
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
<WidgetRowHeader
|
||||
id={id}
|
||||
rowWidgetProperties={rowWidgetProperties}
|
||||
setCurrentSelectRowId={setCurrentSelectRowId}
|
||||
setIsDeleteModalOpen={setIsDeleteModalOpen}
|
||||
setIsSettingsModalOpen={setIsSettingsModalOpen}
|
||||
editWidget={editWidget}
|
||||
deleteWidget={deleteWidget}
|
||||
/>
|
||||
</Card>
|
||||
</div>
|
||||
</CardContainer>
|
||||
);
|
||||
})}
|
||||
</ReactGridLayout>
|
||||
<Modal
|
||||
open={isSettingsModalOpen}
|
||||
title="Row Options"
|
||||
destroyOnClose
|
||||
footer={null}
|
||||
onCancel={(): void => {
|
||||
setIsSettingsModalOpen(false);
|
||||
setCurrentSelectRowId(null);
|
||||
}}
|
||||
>
|
||||
<Form form={form} onFinish={onSettingsModalSubmit} requiredMark>
|
||||
<Form.Item required name={['title']}>
|
||||
<Input
|
||||
placeholder="Enter row name here..."
|
||||
defaultValue={defaultTo(
|
||||
widgets?.find((widget) => widget.id === currentSelectRowId)
|
||||
?.title as string,
|
||||
'Sample Title',
|
||||
)}
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item>
|
||||
<Button type="primary" htmlType="submit">
|
||||
}
|
||||
|
||||
return (
|
||||
<CardContainer
|
||||
className={isDashboardLocked ? '' : 'enable-resize'}
|
||||
isDarkMode={isDarkMode}
|
||||
key={id}
|
||||
data-grid={JSON.stringify(currentWidget)}
|
||||
>
|
||||
<Card
|
||||
className="grid-item"
|
||||
isDarkMode={isDarkMode}
|
||||
$panelType={currentWidget?.panelTypes || PANEL_TYPES.TIME_SERIES}
|
||||
>
|
||||
<GridCard
|
||||
widget={(currentWidget as Widgets) || ({ id, query: {} } as Widgets)}
|
||||
headerMenuList={widgetActions}
|
||||
variables={variables}
|
||||
version={selectedDashboard?.data?.version}
|
||||
onDragSelect={onDragSelect}
|
||||
/>
|
||||
</Card>
|
||||
</CardContainer>
|
||||
);
|
||||
})}
|
||||
</ReactGridLayout>
|
||||
{isDashboardLocked && (
|
||||
<div className="footer">
|
||||
<Button
|
||||
type="text"
|
||||
icon={<LockKeyhole size={14} />}
|
||||
className="locked-text"
|
||||
>
|
||||
Locked
|
||||
</Button>
|
||||
<div className="locked-bar" />
|
||||
</div>
|
||||
)}
|
||||
<Modal
|
||||
open={isSettingsModalOpen}
|
||||
title="Rename Section"
|
||||
rootClassName="rename-section"
|
||||
destroyOnClose
|
||||
footer={null}
|
||||
onCancel={(): void => {
|
||||
setIsSettingsModalOpen(false);
|
||||
setCurrentSelectRowId(null);
|
||||
}}
|
||||
>
|
||||
<Form form={form} onFinish={onSettingsModalSubmit} requiredMark>
|
||||
<Typography.Text className="typography">
|
||||
Enter section name
|
||||
</Typography.Text>
|
||||
<Form.Item required name={['title']}>
|
||||
<Input
|
||||
placeholder="Enter row name here..."
|
||||
defaultValue={defaultTo(
|
||||
widgets?.find((widget) => widget.id === currentSelectRowId)
|
||||
?.title as string,
|
||||
'Sample Title',
|
||||
)}
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item>
|
||||
<div className="action-btns">
|
||||
<Button
|
||||
type="primary"
|
||||
htmlType="submit"
|
||||
className="ok-btn"
|
||||
icon={<Check size={14} />}
|
||||
disabled={updateDashboardMutation.isLoading}
|
||||
>
|
||||
Apply Changes
|
||||
</Button>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</Modal>
|
||||
<Modal
|
||||
open={isDeleteModalOpen}
|
||||
title="Delete Row"
|
||||
destroyOnClose
|
||||
onCancel={(): void => {
|
||||
setIsDeleteModalOpen(false);
|
||||
setCurrentSelectRowId(null);
|
||||
}}
|
||||
onOk={(): void => handleRowDelete()}
|
||||
>
|
||||
<Typography.Text>Are you sure you want to delete this row</Typography.Text>
|
||||
</Modal>
|
||||
</FullScreen>
|
||||
</>
|
||||
<Button
|
||||
type="text"
|
||||
className="cancel-btn"
|
||||
icon={<X size={14} />}
|
||||
onClick={(): void => {
|
||||
setIsSettingsModalOpen(false);
|
||||
setCurrentSelectRowId(null);
|
||||
}}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
</div>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</Modal>
|
||||
<Modal
|
||||
open={isDeleteModalOpen}
|
||||
title="Delete Row"
|
||||
destroyOnClose
|
||||
onCancel={(): void => {
|
||||
setIsDeleteModalOpen(false);
|
||||
setCurrentSelectRowId(null);
|
||||
}}
|
||||
onOk={(): void => handleRowDelete()}
|
||||
>
|
||||
<Typography.Text>Are you sure you want to delete this row</Typography.Text>
|
||||
</Modal>
|
||||
</FullScreen>
|
||||
);
|
||||
}
|
||||
|
||||
|
82
frontend/src/container/GridCardLayout/WidgetRow.tsx
Normal file
82
frontend/src/container/GridCardLayout/WidgetRow.tsx
Normal file
@ -0,0 +1,82 @@
|
||||
import { Button, Popover } from 'antd';
|
||||
import { EllipsisIcon, PenLine, X } from 'lucide-react';
|
||||
import { useState } from 'react';
|
||||
import { Layout } from 'react-grid-layout';
|
||||
|
||||
interface WidgetRowHeaderProps {
|
||||
rowWidgetProperties: {
|
||||
widgets: Layout[];
|
||||
collapsed: boolean;
|
||||
};
|
||||
editWidget: boolean;
|
||||
deleteWidget: boolean;
|
||||
setIsSettingsModalOpen: (value: React.SetStateAction<boolean>) => void;
|
||||
setCurrentSelectRowId: (value: React.SetStateAction<string | null>) => void;
|
||||
setIsDeleteModalOpen: (value: React.SetStateAction<boolean>) => void;
|
||||
id: string;
|
||||
}
|
||||
|
||||
export function WidgetRowHeader(props: WidgetRowHeaderProps): JSX.Element {
|
||||
const {
|
||||
rowWidgetProperties,
|
||||
editWidget,
|
||||
deleteWidget,
|
||||
setCurrentSelectRowId,
|
||||
setIsDeleteModalOpen,
|
||||
setIsSettingsModalOpen,
|
||||
id,
|
||||
} = props;
|
||||
const [isRowSettingsOpen, setIsRowSettingsOpen] = useState<boolean>(false);
|
||||
return (
|
||||
<Popover
|
||||
open={isRowSettingsOpen}
|
||||
arrow={false}
|
||||
onOpenChange={(visible): void => setIsRowSettingsOpen(visible)}
|
||||
rootClassName="row-settings"
|
||||
trigger="hover"
|
||||
placement="bottomRight"
|
||||
content={
|
||||
<div className="menu-content">
|
||||
<section className="section-1">
|
||||
<Button
|
||||
className="rename-btn"
|
||||
type="text"
|
||||
disabled={!editWidget}
|
||||
icon={<PenLine size={14} />}
|
||||
onClick={(): void => {
|
||||
setIsSettingsModalOpen(true);
|
||||
setCurrentSelectRowId(id);
|
||||
setIsRowSettingsOpen(false);
|
||||
}}
|
||||
>
|
||||
Rename
|
||||
</Button>
|
||||
</section>
|
||||
{!rowWidgetProperties.collapsed && (
|
||||
<section className="section-2">
|
||||
<Button
|
||||
className="remove-section"
|
||||
type="text"
|
||||
icon={<X size={14} />}
|
||||
disabled={!deleteWidget}
|
||||
onClick={(): void => {
|
||||
setIsDeleteModalOpen(true);
|
||||
setCurrentSelectRowId(id);
|
||||
setIsRowSettingsOpen(false);
|
||||
}}
|
||||
>
|
||||
Remove Section
|
||||
</Button>
|
||||
</section>
|
||||
)}
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<EllipsisIcon
|
||||
size={14}
|
||||
className="settings-icon"
|
||||
onClick={(): void => setIsRowSettingsOpen(!isRowSettingsOpen)}
|
||||
/>
|
||||
</Popover>
|
||||
);
|
||||
}
|
@ -1,16 +1,13 @@
|
||||
import { useDashboard } from 'providers/Dashboard/Dashboard';
|
||||
import { useCallback } from 'react';
|
||||
import { FullScreenHandle } from 'react-full-screen';
|
||||
|
||||
import GraphLayoutContainer from './GridCardLayout';
|
||||
|
||||
function GridGraph(): JSX.Element {
|
||||
const { handleToggleDashboardSlider } = useDashboard();
|
||||
|
||||
const onEmptyWidgetHandler = useCallback(() => {
|
||||
handleToggleDashboardSlider(true);
|
||||
}, [handleToggleDashboardSlider]);
|
||||
|
||||
return <GraphLayoutContainer onAddPanelHandler={onEmptyWidgetHandler} />;
|
||||
interface GridGraphProps {
|
||||
handle: FullScreenHandle;
|
||||
}
|
||||
function GridGraph(props: GridGraphProps): JSX.Element {
|
||||
const { handle } = props;
|
||||
return <GraphLayoutContainer handle={handle} />;
|
||||
}
|
||||
|
||||
export default GridGraph;
|
||||
|
@ -8,12 +8,28 @@ const ReactGridLayoutComponent = WidthProvider(RGL);
|
||||
|
||||
interface CardProps {
|
||||
$panelType: PANEL_TYPES;
|
||||
isDarkMode: boolean;
|
||||
}
|
||||
|
||||
export const Card = styled(CardComponent)<CardProps>`
|
||||
&&& {
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
border-radius: 3px;
|
||||
border: 1px solid var(--bg-slate-500);
|
||||
background: linear-gradient(
|
||||
0deg,
|
||||
rgba(171, 189, 255, 0) 0%,
|
||||
rgba(171, 189, 255, 0) 100%
|
||||
),
|
||||
#0b0c0e;
|
||||
|
||||
${({ isDarkMode }): StyledCSS =>
|
||||
!isDarkMode &&
|
||||
css`
|
||||
border: 1px solid var(--bg-vanilla-300);
|
||||
background: unset;
|
||||
`}
|
||||
}
|
||||
|
||||
.ant-card-body {
|
||||
@ -75,6 +91,7 @@ export const ReactGridLayout = styled(ReactGridLayoutComponent)`
|
||||
margin-top: 1rem;
|
||||
position: relative;
|
||||
min-height: 40vh;
|
||||
margin: 16px;
|
||||
|
||||
.react-grid-item.react-grid-placeholder {
|
||||
background: grey;
|
||||
|
@ -1,3 +0,0 @@
|
||||
export interface GraphLayoutProps {
|
||||
onAddPanelHandler: VoidFunction;
|
||||
}
|
1315
frontend/src/container/ListOfDashboard/DashboardList.styles.scss
Normal file
1315
frontend/src/container/ListOfDashboard/DashboardList.styles.scss
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,269 @@
|
||||
.new-dashboard-templates-modal {
|
||||
.ant-modal-content {
|
||||
border-radius: 4px;
|
||||
border: 1px solid var(--bg-slate-400);
|
||||
background: linear-gradient(
|
||||
139deg,
|
||||
rgba(18, 19, 23, 0.8) 0%,
|
||||
rgba(18, 19, 23, 0.9) 98.68%
|
||||
);
|
||||
box-shadow: 4px 10px 16px 2px rgba(0, 0, 0, 0.2);
|
||||
backdrop-filter: blur(20px);
|
||||
|
||||
padding: 0;
|
||||
height: 72vh;
|
||||
|
||||
.ant-modal-body {
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.new-dashboard-templates-content-container {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.new-dashboard-templates-content-header {
|
||||
padding: 16px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
border-bottom: 1px solid var(--bg-slate-500);
|
||||
height: 60px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.new-dashboard-templates-content {
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
position: relative;
|
||||
|
||||
height: calc(100% - 60px);
|
||||
|
||||
.new-dashboard-templates-list {
|
||||
padding: 16px 8px;
|
||||
height: 100%;
|
||||
width: 25%;
|
||||
border-right: 1px solid var(--bg-slate-500);
|
||||
|
||||
.new-dashboard-templates-search {
|
||||
height: 32px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.templates-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
padding-bottom: 16px;
|
||||
overflow-y: auto;
|
||||
box-sizing: border-box;
|
||||
|
||||
height: calc(100% - 64px);
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
height: 1rem;
|
||||
width: 0.1rem;
|
||||
}
|
||||
|
||||
.template-list-item {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
padding: 4px 12px;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
height: 32px;
|
||||
box-sizing: border-box;
|
||||
|
||||
.template-icon {
|
||||
display: flex;
|
||||
height: 14px;
|
||||
width: 14px;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.template-name {
|
||||
color: #c0c1c3;
|
||||
font-style: normal;
|
||||
font-weight: 300;
|
||||
line-height: 18px;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
border-radius: 3px;
|
||||
background: rgba(171, 189, 255, 0.08);
|
||||
}
|
||||
|
||||
&.active {
|
||||
border-radius: 3px;
|
||||
background: rgba(171, 189, 255, 0.08);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.new-dashboard-template-preview {
|
||||
flex: 1;
|
||||
position: relative;
|
||||
|
||||
.template-preview-header {
|
||||
padding: 16px;
|
||||
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
|
||||
.template-preview-title {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
|
||||
.template-preview-icon {
|
||||
height: 40px;
|
||||
width: 40px;
|
||||
flex-shrink: 0;
|
||||
border-radius: 2px;
|
||||
border: 1px solid var(--bg-ink-50);
|
||||
background: var(--bg-ink-300);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.template-info {
|
||||
.template-name {
|
||||
color: var(--bg-vanilla-100);
|
||||
font-family: Inter;
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
line-height: 20px; /* 142.857% */
|
||||
letter-spacing: -0.07px;
|
||||
}
|
||||
|
||||
.template-description {
|
||||
color: var(--bg-vanilla-400);
|
||||
font-family: Inter;
|
||||
font-size: 12px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 18px; /* 150% */
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.template-preview-image {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
margin: 24px;
|
||||
height: calc(100% - 144px);
|
||||
position: relative;
|
||||
|
||||
img {
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
padding: 24px;
|
||||
border: 1px solid var(--bg-ink-50);
|
||||
background: var(--bg-ink-300);
|
||||
max-height: 100%;
|
||||
object-fit: contain;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.new-dashboard-templates-modal-footer {
|
||||
.create-dashboard-json-error {
|
||||
margin-bottom: 8px;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.action-btns-container {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
}
|
||||
|
||||
.ant-modal-footer {
|
||||
margin-top: 0;
|
||||
padding: 16px;
|
||||
border-top: 1px solid var(--bg-slate-500);
|
||||
}
|
||||
}
|
||||
|
||||
.lightMode {
|
||||
.new-dashboard-templates-modal {
|
||||
.ant-modal-content {
|
||||
border: 1px solid var(--bg-vanilla-300);
|
||||
background: var(--bg-vanilla-100);
|
||||
}
|
||||
|
||||
.new-dashboard-templates-content-header {
|
||||
border-bottom: 1px solid var(--bg-vanilla-300);
|
||||
}
|
||||
|
||||
.new-dashboard-templates-content {
|
||||
.new-dashboard-templates-list {
|
||||
border-right: 1px solid var(--bg-vanilla-300);
|
||||
|
||||
.templates-list {
|
||||
.template-list-item {
|
||||
.template-name {
|
||||
color: var(--bg-ink-300);
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: rgba(171, 189, 255, 0.08);
|
||||
}
|
||||
|
||||
&.active {
|
||||
background: rgba(171, 189, 255, 0.08);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.new-dashboard-template-preview {
|
||||
.template-preview-header {
|
||||
.template-preview-title {
|
||||
.template-preview-icon {
|
||||
border: 1px solid var(--bg-vanilla-300);
|
||||
background: var(--bg-vanilla-100);
|
||||
}
|
||||
|
||||
.template-info {
|
||||
.template-name {
|
||||
color: var(--bg-ink-300);
|
||||
}
|
||||
|
||||
.template-description {
|
||||
color: var(--bg-vanilla-400);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.create-dashboard-btn {
|
||||
.ant-btn {
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.template-preview-image {
|
||||
img {
|
||||
border: 1px solid var(--bg-vanilla-300);
|
||||
background: var(--bg-vanilla-100);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ant-modal-footer {
|
||||
border-top: 1px solid var(--bg-vanilla-300);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,225 @@
|
||||
/* eslint-disable sonarjs/no-duplicate-string */
|
||||
/* eslint-disable jsx-a11y/no-static-element-interactions */
|
||||
/* eslint-disable jsx-a11y/click-events-have-key-events */
|
||||
/* eslint-disable @typescript-eslint/explicit-function-return-type */
|
||||
import './DashboardTemplatesModal.styles.scss';
|
||||
|
||||
import { Button, Input, Modal, Typography } from 'antd';
|
||||
import ApacheIcon from 'assets/CustomIcons/ApacheIcon';
|
||||
import DockerIcon from 'assets/CustomIcons/DockerIcon';
|
||||
import ElasticSearchIcon from 'assets/CustomIcons/ElasticSearchIcon';
|
||||
import HerokuIcon from 'assets/CustomIcons/HerokuIcon';
|
||||
import KubernetesIcon from 'assets/CustomIcons/KubernetesIcon';
|
||||
import MongoDBIcon from 'assets/CustomIcons/MongoDBIcon';
|
||||
import MySQLIcon from 'assets/CustomIcons/MySQLIcon';
|
||||
import NginxIcon from 'assets/CustomIcons/NginxIcon';
|
||||
import PostgreSQLIcon from 'assets/CustomIcons/PostgreSQLIcon';
|
||||
import RedisIcon from 'assets/CustomIcons/RedisIcon';
|
||||
import cx from 'classnames';
|
||||
import { ConciergeBell, DraftingCompass, Drill, Plus, X } from 'lucide-react';
|
||||
import { ChangeEvent, useState } from 'react';
|
||||
import { DashboardTemplate } from 'types/api/dashboard/getAll';
|
||||
|
||||
import { filterTemplates } from '../utils';
|
||||
|
||||
const templatesList: DashboardTemplate[] = [
|
||||
{
|
||||
name: 'Blank dashboard',
|
||||
icon: <Drill />,
|
||||
id: 'blank',
|
||||
description: 'Create a custom dashboard from scratch.',
|
||||
previewImage: '/Images/blankDashboardTemplatePreview.svg',
|
||||
},
|
||||
{
|
||||
name: 'Alert Manager',
|
||||
icon: <ConciergeBell />,
|
||||
id: 'alertManager',
|
||||
description: 'Create a custom dashboard from scratch.',
|
||||
previewImage: '/Images/blankDashboardTemplatePreview.svg',
|
||||
},
|
||||
{
|
||||
name: 'Apache',
|
||||
icon: <ApacheIcon />,
|
||||
id: 'apache',
|
||||
description: 'Create a custom dashboard from scratch.',
|
||||
previewImage: '/Images/blankDashboardTemplatePreview.svg',
|
||||
},
|
||||
{
|
||||
name: 'Docker',
|
||||
icon: <DockerIcon />,
|
||||
id: 'docker',
|
||||
description: 'Create a custom dashboard from scratch.',
|
||||
previewImage: '/Images/blankDashboardTemplatePreview.svg',
|
||||
},
|
||||
{
|
||||
name: 'Elasticsearch',
|
||||
icon: <ElasticSearchIcon />,
|
||||
id: 'elasticSearch',
|
||||
description: 'Create a custom dashboard from scratch.',
|
||||
previewImage: '/Images/blankDashboardTemplatePreview.svg',
|
||||
},
|
||||
{
|
||||
name: 'MongoDB',
|
||||
icon: <MongoDBIcon />,
|
||||
id: 'mongoDB',
|
||||
description: 'Create a custom dashboard from scratch.',
|
||||
previewImage: '/Images/blankDashboardTemplatePreview.svg',
|
||||
},
|
||||
{
|
||||
name: 'Heroku',
|
||||
icon: <HerokuIcon />,
|
||||
id: 'heroku',
|
||||
description: 'Create a custom dashboard from scratch.',
|
||||
previewImage: '/Images/blankDashboardTemplatePreview.svg',
|
||||
},
|
||||
{
|
||||
name: 'Nginx',
|
||||
icon: <NginxIcon />,
|
||||
id: 'nginx',
|
||||
description: 'Create a custom dashboard from scratch.',
|
||||
previewImage: '/Images/blankDashboardTemplatePreview.svg',
|
||||
},
|
||||
{
|
||||
name: 'Kubernetes',
|
||||
icon: <KubernetesIcon />,
|
||||
id: 'kubernetes',
|
||||
description: 'Create a custom dashboard from scratch.',
|
||||
previewImage: '/Images/blankDashboardTemplatePreview.svg',
|
||||
},
|
||||
{
|
||||
name: 'MySQL',
|
||||
icon: <MySQLIcon />,
|
||||
id: 'mySQL',
|
||||
description: 'Create a custom dashboard from scratch.',
|
||||
previewImage: '/Images/blankDashboardTemplatePreview.svg',
|
||||
},
|
||||
{
|
||||
name: 'PostgreSQL',
|
||||
icon: <PostgreSQLIcon />,
|
||||
id: 'postgreSQL',
|
||||
description: 'Create a custom dashboard from scratch.',
|
||||
previewImage: '/Images/blankDashboardTemplatePreview.svg',
|
||||
},
|
||||
{
|
||||
name: 'Redis',
|
||||
icon: <RedisIcon />,
|
||||
id: 'redis',
|
||||
description: 'Create a custom dashboard from scratch.',
|
||||
previewImage: '/Images/redisTemplatePreview.svg',
|
||||
},
|
||||
{
|
||||
name: 'AWS',
|
||||
icon: <DraftingCompass size={14} />,
|
||||
id: 'aws',
|
||||
description: 'Create a custom dashboard from scratch.',
|
||||
previewImage: '/Images/blankDashboardTemplatePreview.svg',
|
||||
},
|
||||
];
|
||||
|
||||
interface DashboardTemplatesModalProps {
|
||||
showNewDashboardTemplatesModal: boolean;
|
||||
onCreateNewDashboard: () => void;
|
||||
onCancel: () => void;
|
||||
}
|
||||
|
||||
export default function DashboardTemplatesModal({
|
||||
showNewDashboardTemplatesModal,
|
||||
onCreateNewDashboard,
|
||||
onCancel,
|
||||
}: DashboardTemplatesModalProps): JSX.Element {
|
||||
const [selectedDashboardTemplate, setSelectedDashboardTemplate] = useState(
|
||||
templatesList[0],
|
||||
);
|
||||
|
||||
const [dashboardTemplates, setDashboardTemplates] = useState(templatesList);
|
||||
|
||||
const handleDashboardTemplateSearch = (
|
||||
event: ChangeEvent<HTMLInputElement>,
|
||||
) => {
|
||||
const searchText = event.target.value;
|
||||
const filteredTemplates = filterTemplates(searchText, templatesList);
|
||||
setDashboardTemplates(filteredTemplates);
|
||||
};
|
||||
|
||||
return (
|
||||
<Modal
|
||||
wrapClassName="new-dashboard-templates-modal"
|
||||
open={showNewDashboardTemplatesModal}
|
||||
centered
|
||||
closable={false}
|
||||
footer={null}
|
||||
destroyOnClose
|
||||
width="60vw"
|
||||
>
|
||||
<div className="new-dashboard-templates-content-container">
|
||||
<div className="new-dashboard-templates-content-header">
|
||||
<Typography.Text>New Dashboard</Typography.Text>
|
||||
|
||||
<X size={14} className="periscope-btn ghost" onClick={onCancel} />
|
||||
</div>
|
||||
|
||||
<div className="new-dashboard-templates-content">
|
||||
<div className="new-dashboard-templates-list">
|
||||
<Input
|
||||
className="new-dashboard-templates-search"
|
||||
placeholder="🔍 Search..."
|
||||
onChange={handleDashboardTemplateSearch}
|
||||
/>
|
||||
|
||||
<div className="templates-list">
|
||||
{dashboardTemplates.map((template) => (
|
||||
<div
|
||||
className={cx(
|
||||
'template-list-item',
|
||||
selectedDashboardTemplate.id === template.id ? 'active' : '',
|
||||
)}
|
||||
key={template.name}
|
||||
onClick={() => setSelectedDashboardTemplate(template)}
|
||||
>
|
||||
<div className="template-icon">{template.icon}</div>
|
||||
<div className="template-name">{template.name}</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="new-dashboard-template-preview">
|
||||
<div className="template-preview-header">
|
||||
<div className="template-preview-title">
|
||||
<div className="template-preview-icon">
|
||||
{selectedDashboardTemplate.icon}
|
||||
</div>
|
||||
|
||||
<div className="template-info">
|
||||
<div className="template-name">{selectedDashboardTemplate.name}</div>
|
||||
|
||||
<div className="template-description">
|
||||
{selectedDashboardTemplate.description}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="create-dashboard-btn">
|
||||
<Button
|
||||
type="primary"
|
||||
className="periscope-btn primary"
|
||||
icon={<Plus size={14} />}
|
||||
onClick={onCreateNewDashboard}
|
||||
>
|
||||
New dashboard
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="template-preview-image">
|
||||
<img
|
||||
src={selectedDashboardTemplate.previewImage}
|
||||
alt={`${selectedDashboardTemplate.name}-preview`}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Modal>
|
||||
);
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,88 @@
|
||||
.import-json-modal {
|
||||
.ant-modal-content {
|
||||
border-radius: 4px;
|
||||
border: 1px solid var(--bg-slate-400);
|
||||
background: linear-gradient(
|
||||
139deg,
|
||||
rgba(18, 19, 23, 0.8) 0%,
|
||||
rgba(18, 19, 23, 0.9) 98.68%
|
||||
);
|
||||
box-shadow: 4px 10px 16px 2px rgba(0, 0, 0, 0.2);
|
||||
backdrop-filter: blur(20px);
|
||||
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.margin {
|
||||
background: linear-gradient(
|
||||
139deg,
|
||||
rgba(18, 19, 23, 0.8) 0%,
|
||||
rgba(18, 19, 23, 0.9) 98.68%
|
||||
);
|
||||
|
||||
backdrop-filter: blur(20px);
|
||||
}
|
||||
.view-lines {
|
||||
background: linear-gradient(
|
||||
139deg,
|
||||
rgba(18, 19, 23, 0.8) 0%,
|
||||
rgba(18, 19, 23, 0.9) 98.68%
|
||||
);
|
||||
|
||||
backdrop-filter: blur(20px);
|
||||
}
|
||||
|
||||
.import-json-content-header {
|
||||
padding: 16px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
border-bottom: 1px solid var(--bg-slate-500);
|
||||
}
|
||||
|
||||
.import-json-modal-footer {
|
||||
.create-dashboard-json-error {
|
||||
margin-bottom: 8px;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.action-btns-container {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
}
|
||||
|
||||
.ant-modal-footer {
|
||||
margin-top: 0;
|
||||
padding: 16px;
|
||||
border-top: 1px solid var(--bg-slate-500);
|
||||
}
|
||||
}
|
||||
|
||||
.lightMode {
|
||||
.import-json-modal {
|
||||
.ant-modal-content {
|
||||
border: 1px solid var(--bg-vanilla-300);
|
||||
background: var(--bg-vanilla-100);
|
||||
}
|
||||
|
||||
.margin {
|
||||
background: var(--bg-vanilla-100);
|
||||
}
|
||||
.view-lines {
|
||||
background: var(--bg-vanilla-100);
|
||||
}
|
||||
|
||||
.import-json-content-header {
|
||||
border-bottom: 1px solid var(--bg-vanilla-300);
|
||||
}
|
||||
|
||||
.ant-modal-footer {
|
||||
border-top: 1px solid var(--bg-vanilla-300);
|
||||
|
||||
.ant-btn {
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,20 +1,23 @@
|
||||
import './importJSON.styles.scss';
|
||||
|
||||
import { red } from '@ant-design/colors';
|
||||
import { ExclamationCircleTwoTone } from '@ant-design/icons';
|
||||
import MEditor, { Monaco } from '@monaco-editor/react';
|
||||
import { Color } from '@signozhq/design-tokens';
|
||||
import { Button, Modal, Space, Typography, Upload, UploadProps } from 'antd';
|
||||
import createDashboard from 'api/dashboard/create';
|
||||
import Editor from 'components/Editor';
|
||||
import ROUTES from 'constants/routes';
|
||||
import { useIsDarkMode } from 'hooks/useDarkMode';
|
||||
import { MESSAGE } from 'hooks/useFeatureFlag';
|
||||
import { useNotifications } from 'hooks/useNotifications';
|
||||
import { getUpdatedLayout } from 'lib/dashboard/getUpdatedLayout';
|
||||
import history from 'lib/history';
|
||||
import { MonitorDot, MoveRight, X } from 'lucide-react';
|
||||
import { useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { generatePath } from 'react-router-dom';
|
||||
import { DashboardData } from 'types/api/dashboard/getAll';
|
||||
|
||||
import { EditorContainer, FooterContainer } from './styles';
|
||||
|
||||
function ImportJSON({
|
||||
isImportJSONModalVisible,
|
||||
uploadedGrafana,
|
||||
@ -125,62 +128,114 @@ function ImportJSON({
|
||||
onModalHandler();
|
||||
};
|
||||
|
||||
const isDarkMode = useIsDarkMode();
|
||||
|
||||
function setEditorTheme(monaco: Monaco): void {
|
||||
monaco.editor.defineTheme('my-theme', {
|
||||
base: 'vs-dark',
|
||||
inherit: true,
|
||||
rules: [
|
||||
{ token: 'string.key.json', foreground: Color.BG_VANILLA_400 },
|
||||
{ token: 'string.value.json', foreground: Color.BG_ROBIN_400 },
|
||||
],
|
||||
colors: {
|
||||
'editor.background': Color.BG_INK_300,
|
||||
},
|
||||
fontFamily: 'Space Mono',
|
||||
fontSize: 20,
|
||||
fontWeight: 'normal',
|
||||
lineHeight: 18,
|
||||
letterSpacing: -0.06,
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<Modal
|
||||
wrapClassName="import-json-modal"
|
||||
open={isImportJSONModalVisible}
|
||||
centered
|
||||
maskClosable
|
||||
closable={false}
|
||||
destroyOnClose
|
||||
width="70vw"
|
||||
onCancel={onCancelHandler}
|
||||
title={
|
||||
<>
|
||||
<Typography.Title level={4}>{t('import_json')}</Typography.Title>
|
||||
<Typography>{t('import_dashboard_by_pasting')}</Typography>
|
||||
</>
|
||||
}
|
||||
width="60vw"
|
||||
footer={
|
||||
<FooterContainer>
|
||||
<Button
|
||||
disabled={editorValue.length === 0}
|
||||
onClick={onClickLoadJsonHandler}
|
||||
loading={dashboardCreating}
|
||||
>
|
||||
{t('load_json')}
|
||||
</Button>
|
||||
{isCreateDashboardError && getErrorNode(t('error_loading_json'))}
|
||||
{isFeatureAlert && (
|
||||
<Typography.Text type="danger">
|
||||
{MESSAGE.CREATE_DASHBOARD}
|
||||
</Typography.Text>
|
||||
<div className="import-json-modal-footer">
|
||||
{isCreateDashboardError && (
|
||||
<div className="create-dashboard-json-error">
|
||||
{getErrorNode(t('error_loading_json'))}
|
||||
</div>
|
||||
)}
|
||||
</FooterContainer>
|
||||
|
||||
{isUploadJSONError && (
|
||||
<div className="create-dashboard-json-error">
|
||||
{getErrorNode(t('error_upload_json'))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="action-btns-container">
|
||||
<Upload
|
||||
accept=".json"
|
||||
showUploadList={false}
|
||||
multiple={false}
|
||||
onChange={onChangeHandler}
|
||||
beforeUpload={(): boolean => false}
|
||||
action="none"
|
||||
data={jsonData}
|
||||
>
|
||||
<Button
|
||||
type="default"
|
||||
className="periscope-btn"
|
||||
icon={<MonitorDot size={14} />}
|
||||
>
|
||||
{' '}
|
||||
{t('upload_json_file')}
|
||||
</Button>
|
||||
</Upload>
|
||||
|
||||
<Button
|
||||
// disabled={editorValue.length === 0}
|
||||
onClick={onClickLoadJsonHandler}
|
||||
loading={dashboardCreating}
|
||||
className="periscope-btn primary"
|
||||
type="primary"
|
||||
>
|
||||
{t('import_and_next')} <MoveRight size={14} />
|
||||
</Button>
|
||||
|
||||
{isFeatureAlert && (
|
||||
<Typography.Text type="danger">
|
||||
{MESSAGE.CREATE_DASHBOARD}
|
||||
</Typography.Text>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<div>
|
||||
<Space direction="horizontal">
|
||||
<Upload
|
||||
accept=".json"
|
||||
showUploadList={false}
|
||||
multiple={false}
|
||||
onChange={onChangeHandler}
|
||||
beforeUpload={(): boolean => false}
|
||||
action="none"
|
||||
data={jsonData}
|
||||
>
|
||||
<Button type="primary">{t('upload_json_file')}</Button>
|
||||
</Upload>
|
||||
{isUploadJSONError && <>{getErrorNode(t('error_upload_json'))}</>}
|
||||
</Space>
|
||||
<div className="import-json-content-container">
|
||||
<div className="import-json-content-header">
|
||||
<Typography.Text>{t('import_json')}</Typography.Text>
|
||||
|
||||
<EditorContainer>
|
||||
<Typography.Paragraph>{t('paste_json_below')}</Typography.Paragraph>
|
||||
<Editor
|
||||
onChange={(newValue): void => setEditorValue(newValue)}
|
||||
value={editorValue}
|
||||
language="json"
|
||||
/>
|
||||
</EditorContainer>
|
||||
<X size={14} className="periscope-btn ghost" onClick={onCancelHandler} />
|
||||
</div>
|
||||
|
||||
<MEditor
|
||||
language="json"
|
||||
height="40vh"
|
||||
onChange={(newValue): void => setEditorValue(newValue || '')}
|
||||
value={editorValue}
|
||||
options={{
|
||||
scrollbar: {
|
||||
alwaysConsumeMouseWheel: false,
|
||||
},
|
||||
minimap: {
|
||||
enabled: false,
|
||||
},
|
||||
fontSize: 14,
|
||||
fontFamily: 'Space Mono',
|
||||
}}
|
||||
theme={isDarkMode ? 'my-theme' : 'light'}
|
||||
// eslint-disable-next-line react/jsx-no-bind
|
||||
beforeMount={setEditorTheme}
|
||||
/>
|
||||
</div>
|
||||
</Modal>
|
||||
);
|
||||
|
@ -1,10 +0,0 @@
|
||||
import { Space } from 'antd';
|
||||
import styled from 'styled-components';
|
||||
|
||||
export const EditorContainer = styled.div`
|
||||
margin-top: 2rem;
|
||||
`;
|
||||
|
||||
export const FooterContainer = styled(Space)`
|
||||
display: flex;
|
||||
`;
|
@ -3,3 +3,13 @@
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
|
||||
.delete-btn:hover {
|
||||
background-color: rgba(255, 255, 255, 0.12);
|
||||
}
|
||||
|
||||
.lightMode {
|
||||
.delete-btn:hover {
|
||||
background-color: rgba(0, 0, 0, 0.06) !important;
|
||||
}
|
||||
}
|
||||
|
@ -3,8 +3,10 @@ import './DeleteButton.styles.scss';
|
||||
import { DeleteOutlined, ExclamationCircleOutlined } from '@ant-design/icons';
|
||||
import { Modal, Tooltip, Typography } from 'antd';
|
||||
import { REACT_QUERY_KEY } from 'constants/reactQueryKeys';
|
||||
import ROUTES from 'constants/routes';
|
||||
import { useDeleteDashboard } from 'hooks/dashboard/useDeleteDashboard';
|
||||
import { useNotifications } from 'hooks/useNotifications';
|
||||
import history from 'lib/history';
|
||||
import { useCallback } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useQueryClient } from 'react-query';
|
||||
@ -21,13 +23,15 @@ interface DeleteButtonProps {
|
||||
name: string;
|
||||
id: string;
|
||||
isLocked: boolean;
|
||||
routeToListPage?: boolean;
|
||||
}
|
||||
|
||||
function DeleteButton({
|
||||
export function DeleteButton({
|
||||
createdBy,
|
||||
name,
|
||||
id,
|
||||
isLocked,
|
||||
routeToListPage,
|
||||
}: DeleteButtonProps): JSX.Element {
|
||||
const [modal, contextHolder] = Modal.useModal();
|
||||
const { role, user } = useSelector<AppState, AppReducer>((state) => state.app);
|
||||
@ -42,7 +46,7 @@ function DeleteButton({
|
||||
const deleteDashboardMutation = useDeleteDashboard(id);
|
||||
|
||||
const openConfirmationDialog = useCallback((): void => {
|
||||
modal.confirm({
|
||||
const { destroy } = modal.confirm({
|
||||
title: (
|
||||
<Typography.Title level={5}>
|
||||
Are you sure you want to delete the
|
||||
@ -51,24 +55,40 @@ function DeleteButton({
|
||||
</Typography.Title>
|
||||
),
|
||||
icon: <ExclamationCircleOutlined style={{ color: '#e42b35' }} />,
|
||||
onOk() {
|
||||
deleteDashboardMutation.mutateAsync(undefined, {
|
||||
onSuccess: () => {
|
||||
notifications.success({
|
||||
message: t('dashboard:delete_dashboard_success', {
|
||||
name,
|
||||
}),
|
||||
});
|
||||
queryClient.invalidateQueries([REACT_QUERY_KEY.GET_ALL_DASHBOARDS]);
|
||||
},
|
||||
});
|
||||
},
|
||||
okText: 'Delete',
|
||||
okButtonProps: { danger: true },
|
||||
okButtonProps: {
|
||||
danger: true,
|
||||
onClick: (e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
deleteDashboardMutation.mutateAsync(undefined, {
|
||||
onSuccess: () => {
|
||||
notifications.success({
|
||||
message: t('dashboard:delete_dashboard_success', {
|
||||
name,
|
||||
}),
|
||||
});
|
||||
queryClient.invalidateQueries([REACT_QUERY_KEY.GET_ALL_DASHBOARDS]);
|
||||
if (routeToListPage) {
|
||||
history.replace(ROUTES.ALL_DASHBOARD);
|
||||
}
|
||||
destroy();
|
||||
},
|
||||
});
|
||||
},
|
||||
},
|
||||
centered: true,
|
||||
className: 'delete-modal',
|
||||
});
|
||||
}, [modal, name, deleteDashboardMutation, notifications, t, queryClient]);
|
||||
}, [
|
||||
modal,
|
||||
name,
|
||||
deleteDashboardMutation,
|
||||
notifications,
|
||||
t,
|
||||
queryClient,
|
||||
routeToListPage,
|
||||
]);
|
||||
|
||||
const getDeleteTooltipContent = (): string => {
|
||||
if (isLocked) {
|
||||
@ -87,14 +107,17 @@ function DeleteButton({
|
||||
<Tooltip placement="left" title={getDeleteTooltipContent()}>
|
||||
<TableLinkText
|
||||
type="danger"
|
||||
onClick={(): void => {
|
||||
onClick={(e): void => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
if (!isLocked) {
|
||||
openConfirmationDialog();
|
||||
}
|
||||
}}
|
||||
disabled={isLocked}
|
||||
className="delete-btn"
|
||||
disabled={isLocked || (role === USER_ROLES.VIEWER && !isAuthor)}
|
||||
>
|
||||
<DeleteOutlined /> Delete
|
||||
<DeleteOutlined /> Delete dashboard
|
||||
</TableLinkText>
|
||||
</Tooltip>
|
||||
|
||||
@ -103,6 +126,10 @@ function DeleteButton({
|
||||
);
|
||||
}
|
||||
|
||||
DeleteButton.defaultProps = {
|
||||
routeToListPage: false,
|
||||
};
|
||||
|
||||
// This is to avoid the type collision
|
||||
function Wrapper(props: Data): JSX.Element {
|
||||
const {
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { Dashboard } from 'types/api/dashboard/getAll';
|
||||
import { Dashboard, DashboardTemplate } from 'types/api/dashboard/getAll';
|
||||
|
||||
export const filterDashboard = (
|
||||
searchValue: string,
|
||||
@ -25,3 +25,31 @@ export const filterDashboard = (
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
export const filterTemplates = (
|
||||
searchValue: string,
|
||||
dashboardList: DashboardTemplate[],
|
||||
): DashboardTemplate[] => {
|
||||
const searchValueLowerCase = searchValue?.toLowerCase();
|
||||
|
||||
return dashboardList.filter((item: DashboardTemplate) => {
|
||||
const { name } = item;
|
||||
|
||||
// Check if any property value contains the searchValue
|
||||
return name.toLowerCase().includes(searchValueLowerCase);
|
||||
});
|
||||
};
|
||||
|
||||
export interface DashboardDynamicColumns {
|
||||
createdAt: boolean;
|
||||
createdBy: boolean;
|
||||
updatedAt: boolean;
|
||||
updatedBy: boolean;
|
||||
}
|
||||
|
||||
export enum DynamicColumns {
|
||||
CREATED_AT = 'createdAt',
|
||||
CREATED_BY = 'createdBy',
|
||||
UPDATED_AT = 'updatedAt',
|
||||
UPDATED_BY = 'updatedBy',
|
||||
}
|
||||
|
@ -0,0 +1,161 @@
|
||||
.graph-selection {
|
||||
.ant-modal-content {
|
||||
width: 515px;
|
||||
max-height: 646px;
|
||||
overflow-y: auto;
|
||||
flex-shrink: 0;
|
||||
border-radius: 4px;
|
||||
border: 1px solid var(--bg-slate-500);
|
||||
background: var(--bg-ink-400);
|
||||
box-shadow: 0px -4px 16px 2px rgba(0, 0, 0, 0.2);
|
||||
padding: 0px;
|
||||
|
||||
.ant-modal-header {
|
||||
height: 52px;
|
||||
padding: 16px;
|
||||
background: var(--bg-ink-400);
|
||||
border-bottom: 1px solid var(--bg-slate-500);
|
||||
margin-bottom: 0px;
|
||||
|
||||
.ant-modal-title {
|
||||
color: var(--bg-vanilla-100);
|
||||
font-family: Inter;
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 20px; /* 142.857% */
|
||||
}
|
||||
}
|
||||
|
||||
.ant-modal-body {
|
||||
.panel-selection {
|
||||
display: flex;
|
||||
flex-flow: wrap;
|
||||
padding: 16px;
|
||||
gap: 16px;
|
||||
|
||||
.selected {
|
||||
background: var(--bg-slate-400);
|
||||
}
|
||||
|
||||
.ant-card {
|
||||
display: flex;
|
||||
height: 80px;
|
||||
width: 232px;
|
||||
padding: 19px 0px;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
border: 1px solid var(--bg-slate-400);
|
||||
|
||||
.ant-card-body {
|
||||
padding: 0px;
|
||||
border-radius: 0px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 6px;
|
||||
align-items: center;
|
||||
|
||||
.ant-typography {
|
||||
margin-top: 0px;
|
||||
color: var(--bg-vanilla-400);
|
||||
font-family: Inter;
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 20px; /* 142.857% */
|
||||
letter-spacing: -0.07px;
|
||||
}
|
||||
}
|
||||
|
||||
.ant-card-body::before {
|
||||
content: none;
|
||||
}
|
||||
.ant-card-body::after {
|
||||
content: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ant-modal-footer {
|
||||
border-radius: 0px 0px 4px 4px;
|
||||
border-top: 1px solid var(--bg-slate-500);
|
||||
background: var(--bg-ink-400);
|
||||
padding: 12px 15px;
|
||||
margin-top: 0px;
|
||||
|
||||
.ant-btn {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-direction: row-reverse;
|
||||
justify-content: center;
|
||||
color: var(--bg-vanilla-100);
|
||||
|
||||
/* button/ small */
|
||||
font-family: Inter;
|
||||
font-size: 12px;
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
line-height: 24px; /* 200% */
|
||||
border-radius: 2px;
|
||||
background: var(--bg-robin-500);
|
||||
padding: 4px 8px;
|
||||
gap: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
width: 0.1rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.lightMode {
|
||||
.graph-selection {
|
||||
.ant-modal-content {
|
||||
border: 1px solid var(--bg-vanilla-300);
|
||||
background: var(--bg-vanilla-100);
|
||||
|
||||
.ant-modal-header {
|
||||
background: var(--bg-vanilla-100);
|
||||
border-bottom: 1px solid var(--bg-vanilla-300);
|
||||
|
||||
.ant-modal-title {
|
||||
color: var(--bg-ink-300);
|
||||
}
|
||||
}
|
||||
|
||||
.ant-modal-body {
|
||||
.panel-selection {
|
||||
.selected {
|
||||
background: var(--bg-vanilla-200);
|
||||
}
|
||||
|
||||
.ant-card {
|
||||
border: 1px solid var(--bg-vanilla-300);
|
||||
|
||||
.ant-card-body {
|
||||
.ant-typography {
|
||||
color: var(--bg-ink-200);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ant-modal-footer {
|
||||
border-top: 1px solid var(--bg-vanilla-300);
|
||||
background: var(--bg-vanilla-100);
|
||||
|
||||
.ant-btn {
|
||||
color: var(--bg-vanilla-100);
|
||||
|
||||
background: var(--bg-robin-500);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,3 +1,6 @@
|
||||
import './ComponentSlider.styles.scss';
|
||||
|
||||
import { Card, Modal } from 'antd';
|
||||
import { QueryParams } from 'constants/query';
|
||||
import { PANEL_TYPES } from 'constants/queryBuilder';
|
||||
import createQueryParams from 'lib/createQueryParams';
|
||||
@ -8,10 +11,10 @@ import { v4 as uuid } from 'uuid';
|
||||
|
||||
import { PANEL_TYPES_INITIAL_QUERY } from './constants';
|
||||
import menuItems from './menuItems';
|
||||
import { Card, Container, Text } from './styles';
|
||||
import { Text } from './styles';
|
||||
|
||||
function DashboardGraphSlider(): JSX.Element {
|
||||
const { handleToggleDashboardSlider } = useDashboard();
|
||||
const { handleToggleDashboardSlider, isDashboardSliderOpen } = useDashboard();
|
||||
|
||||
// eslint-disable-next-line sonarjs/cognitive-complexity
|
||||
const onClickHandler = (name: PANEL_TYPES) => (): void => {
|
||||
@ -56,15 +59,29 @@ function DashboardGraphSlider(): JSX.Element {
|
||||
}
|
||||
};
|
||||
|
||||
const handleCardClick = (panelType: PANEL_TYPES): void => {
|
||||
onClickHandler(panelType)();
|
||||
};
|
||||
|
||||
return (
|
||||
<Container>
|
||||
{menuItems.map(({ name, icon, display }) => (
|
||||
<Card onClick={onClickHandler(name)} id={name} key={name}>
|
||||
{icon}
|
||||
<Text>{display}</Text>
|
||||
</Card>
|
||||
))}
|
||||
</Container>
|
||||
<Modal
|
||||
open={isDashboardSliderOpen}
|
||||
onCancel={(): void => {
|
||||
handleToggleDashboardSlider(false);
|
||||
}}
|
||||
rootClassName="graph-selection"
|
||||
footer={null}
|
||||
title="New Panel"
|
||||
>
|
||||
<div className="panel-selection">
|
||||
{menuItems.map(({ name, icon, display }) => (
|
||||
<Card onClick={(): void => handleCardClick(name)} id={name} key={name}>
|
||||
{icon}
|
||||
<Text>{display}</Text>
|
||||
</Card>
|
||||
))}
|
||||
</div>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -12,32 +12,32 @@ import {
|
||||
const Items: ItemsProps[] = [
|
||||
{
|
||||
name: PANEL_TYPES.TIME_SERIES,
|
||||
icon: <LineChart size={32} color={Color.BG_ROBIN_400} />,
|
||||
icon: <LineChart size={16} color={Color.BG_ROBIN_400} />,
|
||||
display: 'Time Series',
|
||||
},
|
||||
{
|
||||
name: PANEL_TYPES.VALUE,
|
||||
icon: <SigmaSquare size={32} color={Color.BG_ROBIN_400} />,
|
||||
icon: <SigmaSquare size={16} color={Color.BG_ROBIN_400} />,
|
||||
display: 'Value',
|
||||
},
|
||||
{
|
||||
name: PANEL_TYPES.TABLE,
|
||||
icon: <Table size={32} color={Color.BG_ROBIN_400} />,
|
||||
icon: <Table size={16} color={Color.BG_ROBIN_400} />,
|
||||
display: 'Table',
|
||||
},
|
||||
{
|
||||
name: PANEL_TYPES.LIST,
|
||||
icon: <List size={32} color={Color.BG_ROBIN_400} />,
|
||||
icon: <List size={16} color={Color.BG_ROBIN_400} />,
|
||||
display: 'List',
|
||||
},
|
||||
{
|
||||
name: PANEL_TYPES.BAR,
|
||||
icon: <BarChart3 size={32} color={Color.BG_ROBIN_400} />,
|
||||
icon: <BarChart3 size={16} color={Color.BG_ROBIN_400} />,
|
||||
display: 'Bar',
|
||||
},
|
||||
{
|
||||
name: PANEL_TYPES.PIE,
|
||||
icon: <PieChart size={32} color={Color.BG_ROBIN_400} />,
|
||||
icon: <PieChart size={16} color={Color.BG_ROBIN_400} />,
|
||||
display: 'Pie',
|
||||
},
|
||||
];
|
||||
|
@ -1,13 +1,230 @@
|
||||
.dashboard-description-container {
|
||||
box-shadow: 0px 4px 16px 0px rgba(0, 0, 0, 0.25);
|
||||
.settings-container-root {
|
||||
.ant-drawer-wrapper-body {
|
||||
border-left: 1px solid var(--bg-slate-500);
|
||||
background: var(--bg-ink-400);
|
||||
box-shadow: -4px 10px 16px 2px rgba(0, 0, 0, 0.2);
|
||||
|
||||
border: 1px solid var(--bg-slate-400, #1d212d);
|
||||
background: var(--bg-ink-400, #121317);
|
||||
box-shadow: 0px 0px 8px 0px rgba(0, 0, 0, 0.1);
|
||||
color: var(--bg-vanilla-400, #c0c1c3);
|
||||
.ant-drawer-header {
|
||||
height: 48px;
|
||||
border-bottom: 1px solid var(--bg-slate-500);
|
||||
padding: 14px 14px 14px 11px;
|
||||
|
||||
.ant-drawer-header-title {
|
||||
gap: 16px;
|
||||
|
||||
.ant-drawer-title {
|
||||
color: var(--bg-vanilla-400);
|
||||
font-family: Inter;
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 20px; /* 142.857% */
|
||||
letter-spacing: -0.07px;
|
||||
padding-left: 16px;
|
||||
border-left: 1px solid #161922;
|
||||
}
|
||||
|
||||
.ant-drawer-close {
|
||||
height: 16px;
|
||||
width: 16px;
|
||||
margin-inline-end: 0px !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ant-drawer-body {
|
||||
padding: 16px;
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
width: 0.1rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.dashboard-description-container {
|
||||
box-shadow: none;
|
||||
border: none;
|
||||
background: unset;
|
||||
box-shadow: none;
|
||||
color: var(--bg-vanilla-400);
|
||||
|
||||
.ant-card-body {
|
||||
padding: 12px 16px;
|
||||
padding: 0px;
|
||||
}
|
||||
|
||||
.dashboard-breadcrumbs {
|
||||
height: 48px;
|
||||
padding: 16px;
|
||||
border-bottom: 1px solid var(--bg-slate-400);
|
||||
display: flex;
|
||||
gap: 6px;
|
||||
align-items: center;
|
||||
|
||||
.dashboard-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
color: var(--bg-vanilla-400);
|
||||
font-family: Inter;
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 20px; /* 142.857% */
|
||||
letter-spacing: -0.07px;
|
||||
padding: 0px;
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
.dashboard-btn:hover {
|
||||
background-color: unset;
|
||||
}
|
||||
|
||||
.id-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0px 2px;
|
||||
border-radius: 2px;
|
||||
background: rgba(113, 144, 249, 0.1);
|
||||
color: var(--bg-robin-400);
|
||||
font-family: Inter;
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 20px; /* 142.857% */
|
||||
height: 20px;
|
||||
|
||||
.ant-btn-icon {
|
||||
margin-inline-end: 4px;
|
||||
}
|
||||
}
|
||||
.id-btn:hover {
|
||||
background: rgba(113, 144, 249, 0.1);
|
||||
color: var(--bg-robin-300);
|
||||
}
|
||||
}
|
||||
|
||||
.dashbord-details {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
.left-section {
|
||||
display: flex;
|
||||
padding: 10px 0px 0px 16px;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
|
||||
.dashboard-title {
|
||||
color: #fff;
|
||||
font-family: Inter;
|
||||
font-size: 16px;
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
line-height: 24px; /* 150% */
|
||||
letter-spacing: -0.08px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.right-section {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 10px 16px 0px 0px;
|
||||
gap: 14px;
|
||||
|
||||
.icons {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 32px;
|
||||
height: 34px;
|
||||
padding: 6px;
|
||||
justify-content: center;
|
||||
border-radius: 2px;
|
||||
border: 1px solid var(--bg-slate-400);
|
||||
background: var(--bg-ink-300);
|
||||
color: var(--bg-vanilla-400);
|
||||
font-family: Inter;
|
||||
font-size: 12px;
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
line-height: 10px; /* 83.333% */
|
||||
letter-spacing: 0.12px;
|
||||
}
|
||||
|
||||
.icons:hover {
|
||||
background-color: unset;
|
||||
}
|
||||
.configure-button {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 93px;
|
||||
height: 34px;
|
||||
padding: 6px;
|
||||
justify-content: center;
|
||||
border-radius: 2px;
|
||||
border: 1px solid var(--bg-slate-400);
|
||||
background: var(--bg-ink-300);
|
||||
color: var(--bg-vanilla-400);
|
||||
font-family: Inter;
|
||||
font-size: 12px;
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
line-height: 10px; /* 83.333% */
|
||||
letter-spacing: 0.12px;
|
||||
}
|
||||
|
||||
.add-panel-btn {
|
||||
display: flex;
|
||||
width: 119px;
|
||||
height: 34px;
|
||||
padding: 5.937px 11.875px;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
color: var(--bg-vanilla-100);
|
||||
font-family: Inter;
|
||||
font-size: 11.875px;
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
line-height: 17.812px; /* 150% */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.dashboard-tags {
|
||||
display: flex;
|
||||
gap: 6px;
|
||||
padding: 16px 16px 0px 16px;
|
||||
.tag {
|
||||
display: flex;
|
||||
padding: 4px 8px;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
border-radius: 20px;
|
||||
border: 1px solid rgba(173, 127, 88, 0.2);
|
||||
background: rgba(173, 127, 88, 0.1);
|
||||
color: var(--bg-sienna-400);
|
||||
text-align: center;
|
||||
font-family: Inter;
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
line-height: 20px; /* 142.857% */
|
||||
letter-spacing: -0.07px;
|
||||
margin-inline-end: 0px;
|
||||
}
|
||||
}
|
||||
.dashboard-description-section {
|
||||
max-width: 957px;
|
||||
color: var(--bg-vanilla-400);
|
||||
font-family: Inter;
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 22px; /* 157.143% */
|
||||
letter-spacing: -0.07px;
|
||||
padding: 20px 16px 0px 16px;
|
||||
}
|
||||
|
||||
.dashboard-variables {
|
||||
padding: 16px 16px 18px 16px;
|
||||
}
|
||||
}
|
||||
|
||||
@ -19,21 +236,467 @@
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.dashboard-actions {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
}
|
||||
.dashboard-settings {
|
||||
width: 191px;
|
||||
height: 302px;
|
||||
flex-shrink: 0;
|
||||
|
||||
.lightMode {
|
||||
.dashboard-description-container {
|
||||
box-shadow: none;
|
||||
border: 1px solid var(--bg-vanilla-300);
|
||||
background-color: rgb(250, 250, 250);
|
||||
color: var(--bg-ink-300);
|
||||
.ant-popover-inner {
|
||||
padding: 0px;
|
||||
border-radius: 4px;
|
||||
border: 1px solid var(--bg-slate-400);
|
||||
background: linear-gradient(
|
||||
139deg,
|
||||
rgba(18, 19, 23, 0.8) 0%,
|
||||
rgba(18, 19, 23, 0.9) 98.68%
|
||||
) !important;
|
||||
box-shadow: 4px 10px 16px 2px rgba(0, 0, 0, 0.2);
|
||||
backdrop-filter: blur(20px);
|
||||
}
|
||||
|
||||
.ant-card-body {
|
||||
padding: 12px 16px;
|
||||
.menu-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.section-1 {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: start;
|
||||
border-bottom: 1px solid #1d212d;
|
||||
|
||||
.ant-btn {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
height: 20px;
|
||||
padding: 16px 18px 18px 14px;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
color: var(--bg-vanilla-400);
|
||||
font-family: Inter;
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: normal;
|
||||
letter-spacing: 0.14px;
|
||||
.ant-btn-icon {
|
||||
margin-inline-end: 0px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.section-2 {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: start;
|
||||
border-bottom: 1px solid #1d212d;
|
||||
|
||||
.ant-btn {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
height: 20px;
|
||||
padding: 16px 18px 18px 14px;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
color: var(--bg-vanilla-400);
|
||||
font-family: Inter;
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: normal;
|
||||
letter-spacing: 0.14px;
|
||||
|
||||
.ant-btn-icon {
|
||||
margin-inline-end: 0px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.delete-dashboard {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: start;
|
||||
|
||||
.ant-typography {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
height: 20px;
|
||||
padding: 16px 18px 18px 14px;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
color: var(--bg-cherry-400) !important;
|
||||
font-family: Inter;
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: normal;
|
||||
letter-spacing: 0.14px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.rename-dashboard {
|
||||
.ant-modal-content {
|
||||
width: 384px;
|
||||
flex-shrink: 0;
|
||||
border-radius: 4px;
|
||||
border: 1px solid var(--bg-slate-500);
|
||||
background: var(--bg-ink-400);
|
||||
box-shadow: 0px -4px 16px 2px rgba(0, 0, 0, 0.2);
|
||||
padding: 0px;
|
||||
|
||||
.ant-modal-header {
|
||||
height: 52px;
|
||||
padding: 16px;
|
||||
background: var(--bg-ink-400);
|
||||
border-bottom: 1px solid var(--bg-slate-500);
|
||||
margin-bottom: 0px;
|
||||
.ant-modal-title {
|
||||
color: var(--bg-vanilla-100);
|
||||
font-family: Inter;
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 20px; /* 142.857% */
|
||||
width: 349px;
|
||||
height: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.ant-modal-body {
|
||||
padding: 16px;
|
||||
|
||||
.dashboard-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
|
||||
.name-text {
|
||||
color: var(--bg-vanilla-100);
|
||||
font-family: Inter;
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
line-height: 20px; /* 142.857% */
|
||||
}
|
||||
|
||||
.dashboard-name-input {
|
||||
display: flex;
|
||||
padding: 6px 6px 6px 8px;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
align-self: stretch;
|
||||
border-radius: 0px 2px 2px 0px;
|
||||
border: 1px solid var(--bg-slate-400);
|
||||
background: var(--bg-ink-300);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ant-modal-footer {
|
||||
padding: 16px;
|
||||
margin-top: 0px;
|
||||
.dashboard-rename {
|
||||
display: flex;
|
||||
flex-direction: row-reverse;
|
||||
gap: 12px;
|
||||
|
||||
.cancel-btn {
|
||||
display: flex;
|
||||
padding: 4px 8px;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
border-radius: 2px;
|
||||
background: var(--bg-slate-500);
|
||||
|
||||
.ant-btn-icon {
|
||||
margin-inline-end: 0px;
|
||||
}
|
||||
}
|
||||
|
||||
.rename-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
display: flex;
|
||||
width: 169px;
|
||||
padding: 4px 8px;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
border-radius: 2px;
|
||||
background: var(--bg-robin-500);
|
||||
|
||||
.ant-btn-icon {
|
||||
margin-inline-end: 0px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.section-naming {
|
||||
.ant-modal-content {
|
||||
width: 384px;
|
||||
flex-shrink: 0;
|
||||
border-radius: 4px;
|
||||
border: 1px solid var(--bg-slate-500);
|
||||
background: var(--bg-ink-400);
|
||||
box-shadow: 0px -4px 16px 2px rgba(0, 0, 0, 0.2);
|
||||
padding: 0px;
|
||||
|
||||
.ant-modal-header {
|
||||
height: 52px;
|
||||
padding: 16px;
|
||||
background: var(--bg-ink-400);
|
||||
border-bottom: 1px solid var(--bg-slate-500);
|
||||
margin-bottom: 0px;
|
||||
.ant-modal-title {
|
||||
color: var(--bg-vanilla-100);
|
||||
font-family: Inter;
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 20px; /* 142.857% */
|
||||
width: 349px;
|
||||
height: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.ant-modal-body {
|
||||
padding: 16px;
|
||||
|
||||
.section-naming-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
|
||||
.name-text {
|
||||
color: var(--bg-vanilla-100);
|
||||
font-family: Inter;
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
line-height: 20px; /* 142.857% */
|
||||
}
|
||||
|
||||
.section-name-input {
|
||||
display: flex;
|
||||
width: 320px;
|
||||
padding: 6px 6px 6px 8px;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
align-self: stretch;
|
||||
border-radius: 0px 2px 2px 0px;
|
||||
border: 1px solid var(--bg-slate-400);
|
||||
background: var(--bg-ink-300);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ant-modal-footer {
|
||||
padding: 16px;
|
||||
margin-top: 0px;
|
||||
.dashboard-rename {
|
||||
display: flex;
|
||||
flex-direction: row-reverse;
|
||||
gap: 12px;
|
||||
|
||||
.cancel-btn {
|
||||
display: flex;
|
||||
padding: 4px 8px;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
border-radius: 2px;
|
||||
background: var(--bg-slate-500);
|
||||
|
||||
.ant-btn-icon {
|
||||
margin-inline-end: 0px;
|
||||
}
|
||||
}
|
||||
|
||||
.rename-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
display: flex;
|
||||
width: 140px;
|
||||
padding: 4px 8px;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
border-radius: 2px;
|
||||
background: var(--bg-robin-500);
|
||||
|
||||
.ant-btn-icon {
|
||||
margin-inline-end: 0px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.lightMode {
|
||||
.settings-container-root {
|
||||
.ant-drawer-wrapper-body {
|
||||
border-left: 1px solid var(--bg-vanilla-300);
|
||||
background: var(--bg-vanilla-100);
|
||||
|
||||
.ant-drawer-header {
|
||||
border-bottom: 1px solid var(--bg-vanilla-300);
|
||||
|
||||
.ant-drawer-header-title {
|
||||
.ant-drawer-title {
|
||||
color: var(--bg-ink-400);
|
||||
border-left: 1px solid var(--bg-vanilla-300);
|
||||
}
|
||||
|
||||
.ant-drawer-close {
|
||||
color: var(--bg-ink-300);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.dashboard-description-container {
|
||||
color: var(--bg-ink-400);
|
||||
|
||||
.dashboard-breadcrumbs {
|
||||
border-bottom: 1px solid var(--bg-vanilla-300);
|
||||
|
||||
.dashboard-btn {
|
||||
color: var(--bg-ink-400);
|
||||
}
|
||||
}
|
||||
|
||||
.dashbord-details {
|
||||
.left-section {
|
||||
.dashboard-title {
|
||||
color: var(--bg-ink-300);
|
||||
}
|
||||
}
|
||||
|
||||
.right-section {
|
||||
.icons {
|
||||
border: 1px solid var(--bg-vanilla-300);
|
||||
background: var(--bg-vanilla-100);
|
||||
color: var(--bg-ink-400);
|
||||
}
|
||||
.configure-button {
|
||||
border: 1px solid var(--bg-vanilla-300);
|
||||
background: var(--bg-vanilla-100);
|
||||
color: var(--bg-ink-400);
|
||||
}
|
||||
|
||||
.add-panel-btn {
|
||||
color: var(--bg-vanilla-100);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.dashboard-description-section {
|
||||
color: var(--bg-ink-400);
|
||||
}
|
||||
}
|
||||
.dashboard-settings {
|
||||
.ant-popover-inner {
|
||||
border: 1px solid var(--bg-vanilla-300);
|
||||
background: var(--bg-vanilla-100) !important;
|
||||
}
|
||||
|
||||
.menu-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.section-1 {
|
||||
border-bottom: 1px solid var(--bg-vanilla-300);
|
||||
|
||||
.ant-btn {
|
||||
color: var(--bg-ink-300);
|
||||
}
|
||||
}
|
||||
.section-2 {
|
||||
border-bottom: 1px solid var(--bg-vanilla-300);
|
||||
|
||||
.ant-btn {
|
||||
color: var(--bg-ink-300);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.rename-dashboard {
|
||||
.ant-modal-content {
|
||||
border: 1px solid var(--bg-vanilla-300);
|
||||
background: var(--bg-vanilla-100);
|
||||
|
||||
.ant-modal-header {
|
||||
background: var(--bg-vanilla-100);
|
||||
border-bottom: 1px solid var(--bg-vanilla-300);
|
||||
|
||||
.ant-modal-title {
|
||||
color: var(--bg-ink-300);
|
||||
}
|
||||
}
|
||||
|
||||
.ant-modal-body {
|
||||
.dashboard-content {
|
||||
.name-text {
|
||||
color: var(--bg-ink-300);
|
||||
}
|
||||
|
||||
.dashboard-name-input {
|
||||
border: 1px solid var(--bg-vanilla-300);
|
||||
background: var(--bg-vanilla-100);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ant-modal-footer {
|
||||
.dashboard-rename {
|
||||
.cancel-btn {
|
||||
background: var(--bg-vanilla-300);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.section-naming {
|
||||
.ant-modal-content {
|
||||
border: 1px solid var(--bg-vanilla-300);
|
||||
background: var(--bg-vanilla-100);
|
||||
|
||||
.ant-modal-header {
|
||||
background: var(--bg-vanilla-100);
|
||||
border-bottom: 1px solid var(--bg-vanilla-300);
|
||||
|
||||
.ant-modal-title {
|
||||
color: var(--bg-ink-300);
|
||||
}
|
||||
}
|
||||
|
||||
.ant-modal-body {
|
||||
.section-naming-content {
|
||||
.name-text {
|
||||
color: var(--bg-ink-300);
|
||||
}
|
||||
|
||||
.section-name-input {
|
||||
border: 1px solid var(--bg-vanilla-300);
|
||||
background: var(--bg-vanilla-100);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ant-modal-footer {
|
||||
.dashboard-rename {
|
||||
.cancel-btn {
|
||||
background: var(--bg-vanilla-300);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,8 @@
|
||||
import { Button, Tooltip } from 'antd';
|
||||
import { Cog } from 'lucide-react';
|
||||
import { useState } from 'react';
|
||||
import './Description.styles.scss';
|
||||
|
||||
import { Button } from 'antd';
|
||||
import ConfigureIcon from 'assets/Integrations/ConfigureIcon';
|
||||
import { useRef, useState } from 'react';
|
||||
|
||||
import DashboardSettingsContent from '../DashboardSettings';
|
||||
import { DrawerContainer } from './styles';
|
||||
@ -8,34 +10,38 @@ import { DrawerContainer } from './styles';
|
||||
function SettingsDrawer({ drawerTitle }: { drawerTitle: string }): JSX.Element {
|
||||
const [visible, setVisible] = useState<boolean>(false);
|
||||
|
||||
const variableViewModeRef = useRef<() => void>();
|
||||
|
||||
const showDrawer = (): void => {
|
||||
setVisible(true);
|
||||
};
|
||||
|
||||
const onClose = (): void => {
|
||||
setVisible(false);
|
||||
variableViewModeRef?.current?.();
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Tooltip title="Configure" placement="left">
|
||||
<Button
|
||||
className="periscope-btn"
|
||||
onClick={showDrawer}
|
||||
style={{ width: '100%' }}
|
||||
data-testid="show-drawer"
|
||||
icon={<Cog size={16} />}
|
||||
/>
|
||||
</Tooltip>
|
||||
<Button
|
||||
type="text"
|
||||
className="configure-button"
|
||||
icon={<ConfigureIcon />}
|
||||
data-testid="show-drawer"
|
||||
onClick={showDrawer}
|
||||
>
|
||||
Configure
|
||||
</Button>
|
||||
|
||||
<DrawerContainer
|
||||
title={drawerTitle}
|
||||
placement="right"
|
||||
width="60%"
|
||||
width="50%"
|
||||
onClose={onClose}
|
||||
open={visible}
|
||||
rootClassName="settings-container-root"
|
||||
>
|
||||
<DashboardSettingsContent />
|
||||
<DashboardSettingsContent variableViewModeRef={variableViewModeRef} />
|
||||
</DrawerContainer>
|
||||
</>
|
||||
);
|
||||
|
@ -1,25 +1,66 @@
|
||||
import './Description.styles.scss';
|
||||
|
||||
import { LockFilled, UnlockFilled } from '@ant-design/icons';
|
||||
import { Button, Card, Col, Row, Tag, Tooltip, Typography } from 'antd';
|
||||
import { PlusOutlined } from '@ant-design/icons';
|
||||
import { Button, Card, Input, Modal, Popover, Tag, Typography } from 'antd';
|
||||
import { SOMETHING_WENT_WRONG } from 'constants/api';
|
||||
import { PANEL_GROUP_TYPES, PANEL_TYPES } from 'constants/queryBuilder';
|
||||
import ROUTES from 'constants/routes';
|
||||
import { DeleteButton } from 'container/ListOfDashboard/TableComponents/DeleteButton';
|
||||
import DateTimeSelectionV2 from 'container/TopNav/DateTimeSelectionV2';
|
||||
import { useUpdateDashboard } from 'hooks/dashboard/useUpdateDashboard';
|
||||
import useComponentPermission from 'hooks/useComponentPermission';
|
||||
import { Share2 } from 'lucide-react';
|
||||
import { useNotifications } from 'hooks/useNotifications';
|
||||
import history from 'lib/history';
|
||||
import { isEmpty } from 'lodash-es';
|
||||
import {
|
||||
Check,
|
||||
ClipboardCopy,
|
||||
Ellipsis,
|
||||
FileJson,
|
||||
FolderKanban,
|
||||
Fullscreen,
|
||||
LayoutGrid,
|
||||
LockKeyhole,
|
||||
PenLine,
|
||||
X,
|
||||
} from 'lucide-react';
|
||||
import { useDashboard } from 'providers/Dashboard/Dashboard';
|
||||
import { useState } from 'react';
|
||||
import { sortLayout } from 'providers/Dashboard/util';
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
import { FullScreenHandle } from 'react-full-screen';
|
||||
import { Layout } from 'react-grid-layout';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { useCopyToClipboard } from 'react-use';
|
||||
import { AppState } from 'store/reducers';
|
||||
import { DashboardData } from 'types/api/dashboard/getAll';
|
||||
import { Dashboard, DashboardData } from 'types/api/dashboard/getAll';
|
||||
import AppReducer from 'types/reducer/app';
|
||||
import { USER_ROLES } from 'types/roles';
|
||||
import { ROLES, USER_ROLES } from 'types/roles';
|
||||
import { ComponentTypes } from 'utils/permission';
|
||||
import { v4 as uuid } from 'uuid';
|
||||
|
||||
import DashboardGraphSlider from '../ComponentsSlider';
|
||||
import { Base64Icons } from '../DashboardSettings/General/utils';
|
||||
import DashboardVariableSelection from '../DashboardVariablesSelection';
|
||||
import SettingsDrawer from './SettingsDrawer';
|
||||
import ShareModal from './ShareModal';
|
||||
import { DEFAULT_ROW_NAME, downloadObjectAsJson } from './utils';
|
||||
|
||||
function DashboardDescription(): JSX.Element {
|
||||
interface DashboardDescriptionProps {
|
||||
handle: FullScreenHandle;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line sonarjs/cognitive-complexity
|
||||
function DashboardDescription(props: DashboardDescriptionProps): JSX.Element {
|
||||
const { handle } = props;
|
||||
const {
|
||||
selectedDashboard,
|
||||
panelMap,
|
||||
setPanelMap,
|
||||
layouts,
|
||||
setLayouts,
|
||||
isDashboardLocked,
|
||||
setSelectedDashboard,
|
||||
handleToggleDashboardSlider,
|
||||
handleDashboardLockToggle,
|
||||
} = useDashboard();
|
||||
|
||||
@ -30,12 +71,30 @@ function DashboardDescription(): JSX.Element {
|
||||
}
|
||||
: ({} as DashboardData);
|
||||
|
||||
const { title = '', tags, description } = selectedData || {};
|
||||
const { title = '', description, tags, image = Base64Icons[0] } =
|
||||
selectedData || {};
|
||||
|
||||
const [openDashboardJSON, setOpenDashboardJSON] = useState<boolean>(false);
|
||||
const [updatedTitle, setUpdatedTitle] = useState<string>(title);
|
||||
|
||||
const { user, role } = useSelector<AppState, AppReducer>((state) => state.app);
|
||||
const [sectionName, setSectionName] = useState<string>(DEFAULT_ROW_NAME);
|
||||
|
||||
const updateDashboardMutation = useUpdateDashboard();
|
||||
|
||||
const { featureResponse, user, role } = useSelector<AppState, AppReducer>(
|
||||
(state) => state.app,
|
||||
);
|
||||
const [editDashboard] = useComponentPermission(['edit_dashboard'], role);
|
||||
const [isDashboardSettingsOpen, setIsDashbordSettingsOpen] = useState<boolean>(
|
||||
false,
|
||||
);
|
||||
|
||||
const [isRenameDashboardOpen, setIsRenameDashboardOpen] = useState<boolean>(
|
||||
false,
|
||||
);
|
||||
|
||||
const [isPanelNameModalOpen, setIsPanelNameModalOpen] = useState<boolean>(
|
||||
false,
|
||||
);
|
||||
|
||||
let isAuthor = false;
|
||||
|
||||
@ -43,91 +102,399 @@ function DashboardDescription(): JSX.Element {
|
||||
isAuthor = selectedDashboard?.created_by === user?.email;
|
||||
}
|
||||
|
||||
const onToggleHandler = (): void => {
|
||||
setOpenDashboardJSON((state) => !state);
|
||||
};
|
||||
let permissions: ComponentTypes[] = ['add_panel'];
|
||||
|
||||
if (isDashboardLocked) {
|
||||
permissions = ['add_panel_locked_dashboard'];
|
||||
}
|
||||
|
||||
const { notifications } = useNotifications();
|
||||
|
||||
const userRole: ROLES | null =
|
||||
selectedDashboard?.created_by === user?.email
|
||||
? (USER_ROLES.AUTHOR as ROLES)
|
||||
: role;
|
||||
|
||||
const [addPanelPermission] = useComponentPermission(permissions, userRole);
|
||||
|
||||
const onEmptyWidgetHandler = useCallback(() => {
|
||||
handleToggleDashboardSlider(true);
|
||||
}, [handleToggleDashboardSlider]);
|
||||
|
||||
const handleLockDashboardToggle = (): void => {
|
||||
setIsDashbordSettingsOpen(false);
|
||||
handleDashboardLockToggle(!isDashboardLocked);
|
||||
};
|
||||
|
||||
const onNameChangeHandler = (): void => {
|
||||
if (!selectedDashboard) {
|
||||
return;
|
||||
}
|
||||
const updatedDashboard = {
|
||||
...selectedDashboard,
|
||||
data: {
|
||||
...selectedDashboard.data,
|
||||
title: updatedTitle,
|
||||
},
|
||||
};
|
||||
updateDashboardMutation.mutate(updatedDashboard, {
|
||||
onSuccess: (updatedDashboard) => {
|
||||
notifications.success({
|
||||
message: 'Dashboard renamed successfully',
|
||||
});
|
||||
setIsRenameDashboardOpen(false);
|
||||
if (updatedDashboard.payload)
|
||||
setSelectedDashboard(updatedDashboard.payload);
|
||||
},
|
||||
onError: () => {
|
||||
notifications.error({
|
||||
message: SOMETHING_WENT_WRONG,
|
||||
});
|
||||
setIsRenameDashboardOpen(true);
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const [state, setCopy] = useCopyToClipboard();
|
||||
|
||||
const { t } = useTranslation(['dashboard', 'common']);
|
||||
|
||||
useEffect(() => {
|
||||
if (state.error) {
|
||||
notifications.error({
|
||||
message: t('something_went_wrong', {
|
||||
ns: 'common',
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
if (state.value) {
|
||||
notifications.success({
|
||||
message: t('success', {
|
||||
ns: 'common',
|
||||
}),
|
||||
});
|
||||
}
|
||||
}, [state.error, state.value, t, notifications]);
|
||||
|
||||
function handleAddRow(): void {
|
||||
if (!selectedDashboard) return;
|
||||
const id = uuid();
|
||||
|
||||
const newRowWidgetMap: { widgets: Layout[]; collapsed: boolean } = {
|
||||
widgets: [],
|
||||
collapsed: false,
|
||||
};
|
||||
const currentRowIdx = 0;
|
||||
for (let j = currentRowIdx; j < layouts.length; j++) {
|
||||
if (!panelMap[layouts[j].i]) {
|
||||
newRowWidgetMap.widgets.push(layouts[j]);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const updatedDashboard: Dashboard = {
|
||||
...selectedDashboard,
|
||||
data: {
|
||||
...selectedDashboard.data,
|
||||
layout: [
|
||||
{
|
||||
i: id,
|
||||
w: 12,
|
||||
minW: 12,
|
||||
minH: 1,
|
||||
maxH: 1,
|
||||
x: 0,
|
||||
h: 1,
|
||||
y: 0,
|
||||
},
|
||||
...layouts.filter((e) => e.i !== PANEL_TYPES.EMPTY_WIDGET),
|
||||
],
|
||||
panelMap: { ...panelMap, [id]: newRowWidgetMap },
|
||||
widgets: [
|
||||
...(selectedDashboard.data.widgets || []),
|
||||
{
|
||||
id,
|
||||
title: sectionName,
|
||||
description: '',
|
||||
panelTypes: PANEL_GROUP_TYPES.ROW,
|
||||
},
|
||||
],
|
||||
},
|
||||
uuid: selectedDashboard.uuid,
|
||||
};
|
||||
|
||||
updateDashboardMutation.mutate(updatedDashboard, {
|
||||
// eslint-disable-next-line sonarjs/no-identical-functions
|
||||
onSuccess: (updatedDashboard) => {
|
||||
if (updatedDashboard.payload) {
|
||||
if (updatedDashboard.payload.data.layout)
|
||||
setLayouts(sortLayout(updatedDashboard.payload.data.layout));
|
||||
setSelectedDashboard(updatedDashboard.payload);
|
||||
setPanelMap(updatedDashboard.payload?.data?.panelMap || {});
|
||||
}
|
||||
|
||||
featureResponse.refetch();
|
||||
setIsPanelNameModalOpen(false);
|
||||
setSectionName(DEFAULT_ROW_NAME);
|
||||
},
|
||||
// eslint-disable-next-line sonarjs/no-identical-functions
|
||||
onError: () => {
|
||||
notifications.error({
|
||||
message: SOMETHING_WENT_WRONG,
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<Card className="dashboard-description-container">
|
||||
<Row gutter={16}>
|
||||
<Col flex={1} span={9}>
|
||||
<Typography.Title
|
||||
level={4}
|
||||
style={{ padding: 0, margin: 0 }}
|
||||
data-testid="dashboard-landing-name"
|
||||
>
|
||||
{isDashboardLocked && (
|
||||
<Tooltip title="Dashboard Locked" placement="top">
|
||||
<LockFilled />
|
||||
</Tooltip>
|
||||
)}
|
||||
{title}
|
||||
</Typography.Title>
|
||||
{description && (
|
||||
<Typography
|
||||
className="dashboard-description"
|
||||
data-testid="dashboard-landing-desc"
|
||||
>
|
||||
{description}
|
||||
</Typography>
|
||||
)}
|
||||
|
||||
{tags && (
|
||||
<div style={{ margin: '0.5rem 0' }}>
|
||||
{tags?.map((tag) => (
|
||||
<Tag key={tag}>{tag}</Tag>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</Col>
|
||||
<Col span={14}>
|
||||
<Row justify="end">
|
||||
<DashboardVariableSelection />
|
||||
</Row>
|
||||
</Col>
|
||||
<Col span={1} style={{ textAlign: 'right' }}>
|
||||
{selectedData && (
|
||||
<ShareModal
|
||||
isJSONModalVisible={openDashboardJSON}
|
||||
onToggleHandler={onToggleHandler}
|
||||
selectedData={selectedData}
|
||||
<section className="dashboard-breadcrumbs">
|
||||
<Button
|
||||
type="text"
|
||||
icon={<LayoutGrid size={14} />}
|
||||
className="dashboard-btn"
|
||||
onClick={(): void => history.push(ROUTES.ALL_DASHBOARD)}
|
||||
>
|
||||
Dashboard /
|
||||
</Button>
|
||||
<Button
|
||||
type="text"
|
||||
className="id-btn"
|
||||
icon={
|
||||
// eslint-disable-next-line jsx-a11y/img-redundant-alt
|
||||
<img
|
||||
src={image}
|
||||
alt="dashboard-image"
|
||||
style={{ height: '14px', width: '14px' }}
|
||||
/>
|
||||
}
|
||||
>
|
||||
{title}
|
||||
</Button>
|
||||
</section>
|
||||
<section className="dashbord-details">
|
||||
<div className="left-section">
|
||||
<img
|
||||
src={image}
|
||||
alt="dashboard-img"
|
||||
style={{ width: '16px', height: '16px' }}
|
||||
/>
|
||||
<Typography.Text className="dashboard-title">{title}</Typography.Text>
|
||||
{isDashboardLocked && <LockKeyhole size={14} />}
|
||||
</div>
|
||||
<div className="right-section">
|
||||
<DateTimeSelectionV2 showAutoRefresh hideShareModal />
|
||||
<Popover
|
||||
open={isDashboardSettingsOpen}
|
||||
arrow={false}
|
||||
onOpenChange={(visible): void => setIsDashbordSettingsOpen(visible)}
|
||||
rootClassName="dashboard-settings"
|
||||
content={
|
||||
<div className="menu-content">
|
||||
<section className="section-1">
|
||||
{(isAuthor || role === USER_ROLES.ADMIN) && (
|
||||
<Button
|
||||
type="text"
|
||||
icon={<LockKeyhole size={14} />}
|
||||
onClick={handleLockDashboardToggle}
|
||||
>
|
||||
{isDashboardLocked ? 'Unlock Dashboard' : 'Lock Dashboard'}
|
||||
</Button>
|
||||
)}
|
||||
|
||||
{!isDashboardLocked && editDashboard && (
|
||||
<Button
|
||||
type="text"
|
||||
icon={<PenLine size={14} />}
|
||||
onClick={(): void => {
|
||||
setIsRenameDashboardOpen(true);
|
||||
setIsDashbordSettingsOpen(false);
|
||||
}}
|
||||
>
|
||||
Rename
|
||||
</Button>
|
||||
)}
|
||||
|
||||
<Button
|
||||
type="text"
|
||||
icon={<Fullscreen size={14} />}
|
||||
onClick={handle.enter}
|
||||
>
|
||||
Full screen
|
||||
</Button>
|
||||
</section>
|
||||
<section className="section-2">
|
||||
{!isDashboardLocked && addPanelPermission && (
|
||||
<Button
|
||||
type="text"
|
||||
icon={<FolderKanban size={14} />}
|
||||
onClick={(): void => {
|
||||
setIsPanelNameModalOpen(true);
|
||||
setIsDashbordSettingsOpen(false);
|
||||
}}
|
||||
>
|
||||
New section
|
||||
</Button>
|
||||
)}
|
||||
|
||||
<Button
|
||||
type="text"
|
||||
icon={<FileJson size={14} />}
|
||||
onClick={(): void => {
|
||||
downloadObjectAsJson(selectedData, selectedData.title);
|
||||
setIsDashbordSettingsOpen(false);
|
||||
}}
|
||||
>
|
||||
Export JSON
|
||||
</Button>
|
||||
<Button
|
||||
type="text"
|
||||
icon={<ClipboardCopy size={14} />}
|
||||
onClick={(): void => {
|
||||
setCopy(JSON.stringify(selectedData, null, 2));
|
||||
setIsDashbordSettingsOpen(false);
|
||||
}}
|
||||
>
|
||||
Copy as JSON
|
||||
</Button>
|
||||
</section>
|
||||
<section className="delete-dashboard">
|
||||
<DeleteButton
|
||||
createdBy={selectedDashboard?.created_by || ''}
|
||||
name={selectedDashboard?.data.title || ''}
|
||||
id={String(selectedDashboard?.uuid) || ''}
|
||||
isLocked={isDashboardLocked}
|
||||
routeToListPage
|
||||
/>
|
||||
</section>
|
||||
</div>
|
||||
}
|
||||
trigger="click"
|
||||
placement="bottomRight"
|
||||
>
|
||||
<Button icon={<Ellipsis size={14} />} type="text" className="icons" />
|
||||
</Popover>
|
||||
{!isDashboardLocked && editDashboard && (
|
||||
<SettingsDrawer drawerTitle="Dashboard Configuration" />
|
||||
)}
|
||||
{!isDashboardLocked && addPanelPermission && (
|
||||
<Button
|
||||
className="add-panel-btn"
|
||||
onClick={onEmptyWidgetHandler}
|
||||
icon={<PlusOutlined />}
|
||||
type="primary"
|
||||
data-testid="add-panel"
|
||||
>
|
||||
New Panel
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
</section>
|
||||
{(tags?.length || 0) > 0 && (
|
||||
<div className="dashboard-tags">
|
||||
{tags?.map((tag) => (
|
||||
<Tag key={tag} className="tag">
|
||||
{tag}
|
||||
</Tag>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
{!isEmpty(description) && (
|
||||
<section className="dashboard-description-section">{description}</section>
|
||||
)}
|
||||
<section className="dashboard-variables">
|
||||
<DashboardVariableSelection />
|
||||
</section>
|
||||
<DashboardGraphSlider />
|
||||
|
||||
<div className="dashboard-actions">
|
||||
{!isDashboardLocked && editDashboard && (
|
||||
<SettingsDrawer drawerTitle={title} />
|
||||
)}
|
||||
|
||||
<Tooltip title="Share" placement="left">
|
||||
<Button
|
||||
className="periscope-btn"
|
||||
style={{ width: '100%' }}
|
||||
onClick={onToggleHandler}
|
||||
icon={<Share2 size={16} />}
|
||||
/>
|
||||
</Tooltip>
|
||||
|
||||
{(isAuthor || role === USER_ROLES.ADMIN) && (
|
||||
<Tooltip
|
||||
placement="left"
|
||||
title={isDashboardLocked ? 'Unlock Dashboard' : 'Lock Dashboard'}
|
||||
>
|
||||
<Button
|
||||
style={{ width: '100%' }}
|
||||
className="periscope-btn"
|
||||
onClick={handleLockDashboardToggle}
|
||||
icon={isDashboardLocked ? <LockFilled /> : <UnlockFilled />}
|
||||
/>
|
||||
</Tooltip>
|
||||
)}
|
||||
<Modal
|
||||
open={isRenameDashboardOpen}
|
||||
title="Rename Dashboard"
|
||||
onOk={(): void => {
|
||||
// handle update dashboard here
|
||||
}}
|
||||
onCancel={(): void => {
|
||||
setIsRenameDashboardOpen(false);
|
||||
}}
|
||||
rootClassName="rename-dashboard"
|
||||
footer={
|
||||
<div className="dashboard-rename">
|
||||
<Button
|
||||
type="primary"
|
||||
icon={<Check size={14} />}
|
||||
className="rename-btn"
|
||||
onClick={onNameChangeHandler}
|
||||
disabled={updateDashboardMutation.isLoading}
|
||||
>
|
||||
Rename Dashboard
|
||||
</Button>
|
||||
<Button
|
||||
type="text"
|
||||
icon={<X size={14} />}
|
||||
className="cancel-btn"
|
||||
onClick={(): void => setIsRenameDashboardOpen(false)}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
</div>
|
||||
</Col>
|
||||
</Row>
|
||||
}
|
||||
>
|
||||
<div className="dashboard-content">
|
||||
<Typography.Text className="name-text">Enter a new name</Typography.Text>
|
||||
<Input
|
||||
data-testid="dashboard-name"
|
||||
className="dashboard-name-input"
|
||||
value={updatedTitle}
|
||||
onChange={(e): void => setUpdatedTitle(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
</Modal>
|
||||
<Modal
|
||||
open={isPanelNameModalOpen}
|
||||
title="New Section"
|
||||
rootClassName="section-naming"
|
||||
onOk={(): void => handleAddRow()}
|
||||
onCancel={(): void => {
|
||||
setIsPanelNameModalOpen(false);
|
||||
setSectionName(DEFAULT_ROW_NAME);
|
||||
}}
|
||||
footer={
|
||||
<div className="dashboard-rename">
|
||||
<Button
|
||||
type="primary"
|
||||
icon={<Check size={14} />}
|
||||
className="rename-btn"
|
||||
onClick={(): void => handleAddRow()}
|
||||
disabled={updateDashboardMutation.isLoading}
|
||||
>
|
||||
Create Section
|
||||
</Button>
|
||||
<Button
|
||||
type="text"
|
||||
icon={<X size={14} />}
|
||||
className="cancel-btn"
|
||||
onClick={(): void => {
|
||||
setIsPanelNameModalOpen(false);
|
||||
setSectionName(DEFAULT_ROW_NAME);
|
||||
}}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<div className="section-naming-content">
|
||||
<Typography.Text className="name-text">Enter Section name</Typography.Text>
|
||||
<Input
|
||||
data-testid="section-name"
|
||||
className="section-name-input"
|
||||
value={sectionName}
|
||||
onChange={(e): void => setSectionName(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
</Modal>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
@ -12,3 +12,5 @@ export function downloadObjectAsJson(
|
||||
downloadAnchorNode.click();
|
||||
downloadAnchorNode.remove();
|
||||
}
|
||||
|
||||
export const DEFAULT_ROW_NAME = 'Sample Row';
|
||||
|
@ -3,3 +3,134 @@
|
||||
color: rgb(207, 19, 34);
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.dashboard-variable-settings-table {
|
||||
.variable-name-drag {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
|
||||
.ant-table-cell {
|
||||
padding: 0px !important;
|
||||
border: none !important;
|
||||
color: var(--bg-robin-400);
|
||||
font-family: 'Space Mono';
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 18px; /* 128.571% */
|
||||
letter-spacing: -0.07px;
|
||||
}
|
||||
}
|
||||
|
||||
.variable-description-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
flex: 1 0 0;
|
||||
|
||||
.variable-description {
|
||||
color: var(--bg-sienna-400);
|
||||
font-family: 'Space Mono';
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 18px; /* 128.571% */
|
||||
letter-spacing: -0.07px;
|
||||
}
|
||||
|
||||
.actions-btns {
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
display: none;
|
||||
|
||||
&:hover {
|
||||
display: inline-flex;
|
||||
}
|
||||
|
||||
.edit-variable-button {
|
||||
width: 26px;
|
||||
height: 22px;
|
||||
border-radius: 2px;
|
||||
display: flex;
|
||||
padding: 4px 6px;
|
||||
align-items: center;
|
||||
gap: 3px;
|
||||
background: var(--bg-slate-400);
|
||||
}
|
||||
|
||||
.delete-variable-button {
|
||||
width: 26px;
|
||||
height: 22px;
|
||||
border-radius: 2px;
|
||||
background: rgba(229, 72, 77, 0.1);
|
||||
display: flex;
|
||||
padding: 4px 6px;
|
||||
align-items: center;
|
||||
gap: 3px;
|
||||
color: red;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ant-table-cell-row-hover {
|
||||
.actions-btns {
|
||||
display: inline-flex;
|
||||
}
|
||||
}
|
||||
|
||||
.ant-table-thead {
|
||||
.ant-table-cell {
|
||||
padding: 8px 12px;
|
||||
border: 1px solid var(--bg-slate-500);
|
||||
background: unset;
|
||||
}
|
||||
|
||||
.ant-table-cell::before {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.ant-table-tbody {
|
||||
.ant-table-cell {
|
||||
padding: 14px;
|
||||
border: 1px solid var(--bg-slate-500);
|
||||
}
|
||||
}
|
||||
|
||||
.ant-table-row {
|
||||
.ant-table-cell:nth-child(even) {
|
||||
background: rgba(22, 25, 34, 0.4);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.lightMode {
|
||||
.dashboard-variable-settings-table {
|
||||
.variable-description-actions {
|
||||
.actions-btns {
|
||||
.edit-variable-button {
|
||||
background: var(--bg-vanilla-300);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ant-table-thead {
|
||||
.ant-table-cell {
|
||||
border: 1px solid var(--bg-vanilla-300);
|
||||
}
|
||||
}
|
||||
|
||||
.ant-table-tbody {
|
||||
.ant-table-cell {
|
||||
border: 1px solid var(--bg-vanilla-300);
|
||||
}
|
||||
}
|
||||
|
||||
.ant-table-row {
|
||||
.ant-table-cell:nth-child(even) {
|
||||
background: var(--bg-vanilla-200);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,69 @@
|
||||
.settings-tabs {
|
||||
.ant-tabs-nav-list {
|
||||
width: 228px;
|
||||
height: 32px;
|
||||
flex-shrink: 0;
|
||||
border-radius: 2px;
|
||||
border: 1px solid var(--bg-slate-400);
|
||||
background: var(--bg-ink-400);
|
||||
box-shadow: 0px 0px 8px 0px rgba(0, 0, 0, 0.1);
|
||||
transition: opacity 0.1s !important;
|
||||
|
||||
.ant-tabs-tab + .ant-tabs-tab {
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
.overview-btn {
|
||||
width: 114px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.variables-btn {
|
||||
width: 114px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.ant-tabs-ink-bar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.ant-tabs-tab-active {
|
||||
.overview-btn {
|
||||
border-radius: 2px 0px 0px 2px;
|
||||
background: var(--bg-slate-400);
|
||||
}
|
||||
|
||||
.variables-btn {
|
||||
border-radius: 2px 0px 0px 2px;
|
||||
background: var(--bg-slate-400);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ant-tabs-nav::before {
|
||||
border-bottom: none;
|
||||
}
|
||||
}
|
||||
|
||||
.lightMode {
|
||||
.settings-tabs {
|
||||
.ant-tabs-nav-list {
|
||||
border: 1px solid var(--bg-vanilla-300);
|
||||
background: var(--bg-vanilla-100);
|
||||
|
||||
.ant-tabs-tab-active {
|
||||
.overview-btn {
|
||||
background: var(--bg-vanilla-300);
|
||||
}
|
||||
|
||||
.variables-btn {
|
||||
background: var(--bg-vanilla-300);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
.tags-input {
|
||||
display: flex;
|
||||
border: none;
|
||||
padding: 0px;
|
||||
width: 183px;
|
||||
height: 24px;
|
||||
padding: 3px 1px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.tag-container {
|
||||
color: var(--bg-sienna-400);
|
||||
font-family: 'Space Mono';
|
||||
font-size: 13px;
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
line-height: 20px; /* 153.846% */
|
||||
letter-spacing: 0.52px;
|
||||
height: 24px;
|
||||
flex-shrink: 0;
|
||||
border-radius: 50px;
|
||||
border: 1px solid rgba(173, 127, 88, 0.2);
|
||||
background: rgba(173, 127, 88, 0.1);
|
||||
padding: 2px 8px;
|
||||
}
|
||||
|
||||
.edit-input {
|
||||
.ant-form-item {
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
import { PlusOutlined } from '@ant-design/icons';
|
||||
import { Col, Tooltip, Typography } from 'antd';
|
||||
import './AddTags.styles.scss';
|
||||
|
||||
import { Col, Tooltip } from 'antd';
|
||||
import Input from 'components/Input';
|
||||
import { Dispatch, SetStateAction, useState } from 'react';
|
||||
|
||||
@ -7,7 +8,6 @@ import { InputContainer, NewTagContainer, TagsContainer } from './styles';
|
||||
|
||||
function AddTags({ tags, setTags }: AddTagsProps): JSX.Element {
|
||||
const [inputValue, setInputValue] = useState<string>('');
|
||||
const [inputVisible, setInputVisible] = useState<boolean>(false);
|
||||
const [editInputIndex, setEditInputIndex] = useState(-1);
|
||||
const [editInputValue, setEditInputValue] = useState('');
|
||||
|
||||
@ -15,7 +15,6 @@ function AddTags({ tags, setTags }: AddTagsProps): JSX.Element {
|
||||
if (inputValue) {
|
||||
setTags([...tags, inputValue]);
|
||||
}
|
||||
setInputVisible(false);
|
||||
setInputValue('');
|
||||
};
|
||||
|
||||
@ -32,10 +31,6 @@ function AddTags({ tags, setTags }: AddTagsProps): JSX.Element {
|
||||
setTags(newTags);
|
||||
};
|
||||
|
||||
const showInput = (): void => {
|
||||
setInputVisible(true);
|
||||
};
|
||||
|
||||
const onChangeHandler = (
|
||||
value: string,
|
||||
func: Dispatch<SetStateAction<string>>,
|
||||
@ -48,7 +43,7 @@ function AddTags({ tags, setTags }: AddTagsProps): JSX.Element {
|
||||
{tags.map((tag, index) => {
|
||||
if (editInputIndex === index) {
|
||||
return (
|
||||
<Col key={tag} lg={4}>
|
||||
<Col key={tag} lg={4} className="edit-input">
|
||||
<Input
|
||||
size="small"
|
||||
value={editInputValue}
|
||||
@ -65,7 +60,12 @@ function AddTags({ tags, setTags }: AddTagsProps): JSX.Element {
|
||||
const isLongTag = tag.length > 20;
|
||||
|
||||
const tagElem = (
|
||||
<NewTagContainer closable key={tag} onClose={(): void => handleClose(tag)}>
|
||||
<NewTagContainer
|
||||
closable
|
||||
key={tag}
|
||||
onClose={(): void => handleClose(tag)}
|
||||
className="tag-container"
|
||||
>
|
||||
<span
|
||||
onDoubleClick={(e): void => {
|
||||
setEditInputIndex(index);
|
||||
@ -87,32 +87,19 @@ function AddTags({ tags, setTags }: AddTagsProps): JSX.Element {
|
||||
);
|
||||
})}
|
||||
|
||||
{inputVisible && (
|
||||
<InputContainer lg={4}>
|
||||
<Input
|
||||
type="text"
|
||||
size="small"
|
||||
value={inputValue}
|
||||
onChangeHandler={(event): void =>
|
||||
onChangeHandler(event.target.value, setInputValue)
|
||||
}
|
||||
onBlurHandler={handleInputConfirm}
|
||||
onPressEnterHandler={handleInputConfirm}
|
||||
/>
|
||||
</InputContainer>
|
||||
)}
|
||||
|
||||
{!inputVisible && (
|
||||
<NewTagContainer icon={<PlusOutlined />} onClick={showInput}>
|
||||
<Typography
|
||||
style={{
|
||||
fontSize: '12px',
|
||||
}}
|
||||
>
|
||||
New Tag
|
||||
</Typography>
|
||||
</NewTagContainer>
|
||||
)}
|
||||
<InputContainer>
|
||||
<Input
|
||||
type="text"
|
||||
value={inputValue}
|
||||
rootClassName="tags-input"
|
||||
placeholder="Start typing your tag name"
|
||||
onChangeHandler={(event): void =>
|
||||
onChangeHandler(event.target.value, setInputValue)
|
||||
}
|
||||
onBlurHandler={handleInputConfirm}
|
||||
onPressEnterHandler={handleInputConfirm}
|
||||
/>
|
||||
</InputContainer>
|
||||
</TagsContainer>
|
||||
);
|
||||
}
|
||||
|
@ -4,6 +4,8 @@ import styled from 'styled-components';
|
||||
export const TagsContainer = styled.div`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-flow: wrap;
|
||||
gap: 6px;
|
||||
`;
|
||||
|
||||
export const NewTagContainer = styled(Tag)`
|
||||
@ -23,4 +25,6 @@ export const InputContainer = styled(Col)`
|
||||
> div {
|
||||
margin: 0;
|
||||
}
|
||||
padding-left: 0px !important;
|
||||
padding-right: 0px !important;
|
||||
`;
|
||||
|
@ -0,0 +1,197 @@
|
||||
.overview-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.overview-settings {
|
||||
border-radius: 3px;
|
||||
border: 1px solid var(--bg-slate-500);
|
||||
padding: 16px !important;
|
||||
|
||||
.name-icon-input {
|
||||
display: flex;
|
||||
.dashboard-image-input {
|
||||
.ant-select-selector {
|
||||
display: flex;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
padding: 6px;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
border-radius: 2px 0px 0px 2px;
|
||||
border: 1px solid var(--bg-slate-400);
|
||||
background: var(--bg-ink-300);
|
||||
|
||||
.ant-select-selection-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.list-item-image {
|
||||
height: 16px;
|
||||
width: 16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.dashboard-name-input {
|
||||
border-radius: 0px 2px 2px 0px;
|
||||
border: 1px solid var(--bg-slate-400);
|
||||
background: var(--bg-ink-300);
|
||||
}
|
||||
}
|
||||
|
||||
.dashboard-name {
|
||||
color: var(--bg-vanilla-400);
|
||||
font-family: Inter;
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 20px; /* 142.857% */
|
||||
}
|
||||
|
||||
.description-text-area {
|
||||
padding: 6px 6px 6px 8px;
|
||||
border-radius: 2px;
|
||||
border: 1px solid var(--bg-slate-400);
|
||||
background: var(--bg-ink-300);
|
||||
}
|
||||
}
|
||||
|
||||
.overview-settings-footer {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
width: -webkit-fill-available;
|
||||
padding: 12px 16px 12px 0px;
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
height: 32px;
|
||||
border-top: 1px solid var(--bg-slate-500);
|
||||
background: var(--bg-ink-400);
|
||||
|
||||
.unsaved {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
|
||||
.unsaved-dot {
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
border-radius: 50px;
|
||||
background: var(--bg-robin-500);
|
||||
box-shadow: 0px 0px 6px 0px rgba(78, 116, 248, 0.4);
|
||||
}
|
||||
.unsaved-changes {
|
||||
color: var(--bg-robin-400);
|
||||
font-family: Inter;
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 24px; /* 171.429% */
|
||||
letter-spacing: -0.07px;
|
||||
}
|
||||
}
|
||||
.footer-action-btns {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
|
||||
.discard-btn {
|
||||
margin: '16px 0';
|
||||
color: var(--bg-vanilla-100);
|
||||
font-family: Inter;
|
||||
font-size: 12px;
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
line-height: 24px;
|
||||
}
|
||||
|
||||
.save-btn {
|
||||
margin: 0px !important;
|
||||
color: var(--bg-vanilla-100);
|
||||
font-family: Inter;
|
||||
font-size: 12px;
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
line-height: 24px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.dashboard-image-input {
|
||||
&.ant-select-dropdown {
|
||||
padding: 0px !important;
|
||||
}
|
||||
|
||||
.ant-select-item {
|
||||
padding: 0px;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
.ant-select-item-option-content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
.list-item-image {
|
||||
height: 16px;
|
||||
width: 16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.lightMode {
|
||||
.overview-content {
|
||||
.overview-settings {
|
||||
border: 1px solid var(--bg-vanilla-300);
|
||||
|
||||
.name-icon-input {
|
||||
.dashboard-image-input {
|
||||
.ant-select-selector {
|
||||
border: 1px solid var(--bg-vanilla-200);
|
||||
background: var(--bg-vanilla-300);
|
||||
}
|
||||
}
|
||||
|
||||
.dashboard-name-input {
|
||||
border: 1px solid var(--bg-vanilla-200);
|
||||
background: var(--bg-vanilla-300);
|
||||
}
|
||||
}
|
||||
|
||||
.dashboard-name {
|
||||
color: var(--bg-ink-400);
|
||||
}
|
||||
|
||||
.description-text-area {
|
||||
border: 1px solid var(--bg-vanilla-300);
|
||||
background: var(--bg-vanilla-300);
|
||||
}
|
||||
}
|
||||
|
||||
.overview-settings-footer {
|
||||
border-top: 1px solid var(--bg-vanilla-300);
|
||||
background: var(--bg-vanilla-100);
|
||||
|
||||
.unsaved {
|
||||
.unsaved-dot {
|
||||
background: var(--bg-robin-500);
|
||||
}
|
||||
.unsaved-changes {
|
||||
color: var(--bg-robin-400);
|
||||
}
|
||||
}
|
||||
.footer-action-btns {
|
||||
.discard-btn {
|
||||
color: var(--bg-ink-300);
|
||||
background-color: var(--bg-vanilla-300);
|
||||
}
|
||||
|
||||
.save-btn {
|
||||
color: var(--bg-vanilla-300);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,14 +1,20 @@
|
||||
import { SaveOutlined } from '@ant-design/icons';
|
||||
import { Col, Input, Space, Typography } from 'antd';
|
||||
import './GeneralSettings.styles.scss';
|
||||
|
||||
import { Col, Input, Select, Space, Typography } from 'antd';
|
||||
import { SOMETHING_WENT_WRONG } from 'constants/api';
|
||||
import AddTags from 'container/NewDashboard/DashboardSettings/General/AddTags';
|
||||
import { useUpdateDashboard } from 'hooks/dashboard/useUpdateDashboard';
|
||||
import { useNotifications } from 'hooks/useNotifications';
|
||||
import { isEqual } from 'lodash-es';
|
||||
import { Check, X } from 'lucide-react';
|
||||
import { useDashboard } from 'providers/Dashboard/Dashboard';
|
||||
import { useState } from 'react';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { Button } from './styles';
|
||||
import { Base64Icons } from './utils';
|
||||
|
||||
const { Option } = Select;
|
||||
|
||||
function GeneralDashboardSettings(): JSX.Element {
|
||||
const { selectedDashboard, setSelectedDashboard } = useDashboard();
|
||||
@ -17,13 +23,18 @@ function GeneralDashboardSettings(): JSX.Element {
|
||||
|
||||
const selectedData = selectedDashboard?.data;
|
||||
|
||||
const { title = '', tags = [], description = '' } = selectedData || {};
|
||||
const { title = '', tags = [], description = '', image = Base64Icons[0] } =
|
||||
selectedData || {};
|
||||
|
||||
const [updatedTitle, setUpdatedTitle] = useState<string>(title);
|
||||
const [updatedTags, setUpdatedTags] = useState<string[]>(tags || []);
|
||||
const [updatedDescription, setUpdatedDescription] = useState(
|
||||
description || '',
|
||||
);
|
||||
const [updatedImage, setUpdatedImage] = useState<string>(image);
|
||||
const [numberOfUnsavedChanges, setNumberOfUnsavedChanges] = useState<number>(
|
||||
0,
|
||||
);
|
||||
|
||||
const { t } = useTranslation('common');
|
||||
|
||||
@ -40,6 +51,7 @@ function GeneralDashboardSettings(): JSX.Element {
|
||||
description: updatedDescription,
|
||||
tags: updatedTags,
|
||||
title: updatedTitle,
|
||||
image: updatedImage,
|
||||
},
|
||||
},
|
||||
{
|
||||
@ -57,48 +69,135 @@ function GeneralDashboardSettings(): JSX.Element {
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<Col>
|
||||
<Space direction="vertical" style={{ width: '100%' }}>
|
||||
<div>
|
||||
<Typography style={{ marginBottom: '0.5rem' }}>Name</Typography>
|
||||
<Input
|
||||
data-testid="dashboard-name"
|
||||
value={updatedTitle}
|
||||
onChange={(e): void => setUpdatedTitle(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
useEffect(() => {
|
||||
let numberOfUnsavedChanges = 0;
|
||||
if (!isEqual(updatedTitle, selectedData?.title)) {
|
||||
numberOfUnsavedChanges += 1;
|
||||
}
|
||||
if (!isEqual(updatedDescription, selectedData?.description)) {
|
||||
numberOfUnsavedChanges += 1;
|
||||
}
|
||||
if (!isEqual(updatedTags, selectedData?.tags)) {
|
||||
numberOfUnsavedChanges += 1;
|
||||
}
|
||||
if (!isEqual(updatedImage, selectedData?.image)) {
|
||||
numberOfUnsavedChanges += 1;
|
||||
}
|
||||
setNumberOfUnsavedChanges(numberOfUnsavedChanges);
|
||||
}, [
|
||||
selectedData?.description,
|
||||
selectedData?.image,
|
||||
selectedData?.tags,
|
||||
selectedData?.title,
|
||||
updatedDescription,
|
||||
updatedImage,
|
||||
updatedTags,
|
||||
updatedTitle,
|
||||
]);
|
||||
|
||||
<div>
|
||||
<Typography style={{ marginBottom: '0.5rem' }}>Description</Typography>
|
||||
<Input.TextArea
|
||||
data-testid="dashboard-desc"
|
||||
rows={5}
|
||||
value={updatedDescription}
|
||||
onChange={(e): void => setUpdatedDescription(e.target.value)}
|
||||
/>
|
||||
const discardHandler = (): void => {
|
||||
setUpdatedTitle(title);
|
||||
setUpdatedImage(image);
|
||||
setUpdatedTags(tags);
|
||||
setUpdatedDescription(description);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="overview-content">
|
||||
<Col className="overview-settings">
|
||||
<Space
|
||||
direction="vertical"
|
||||
style={{
|
||||
width: '100%',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
gap: '21px',
|
||||
}}
|
||||
>
|
||||
<div>
|
||||
<Typography style={{ marginBottom: '0.5rem' }} className="dashboard-name">
|
||||
Dashboard Name
|
||||
</Typography>
|
||||
<section className="name-icon-input">
|
||||
<Select
|
||||
defaultActiveFirstOption
|
||||
data-testid="dashboard-image"
|
||||
suffixIcon={null}
|
||||
rootClassName="dashboard-image-input"
|
||||
value={updatedImage}
|
||||
onChange={(value: string): void => setUpdatedImage(value)}
|
||||
>
|
||||
{Base64Icons.map((icon) => (
|
||||
<Option value={icon} key={icon}>
|
||||
<img src={icon} alt="dashboard-icon" className="list-item-image" />
|
||||
</Option>
|
||||
))}
|
||||
</Select>
|
||||
<Input
|
||||
data-testid="dashboard-name"
|
||||
className="dashboard-name-input"
|
||||
value={updatedTitle}
|
||||
onChange={(e): void => setUpdatedTitle(e.target.value)}
|
||||
/>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Typography style={{ marginBottom: '0.5rem' }} className="dashboard-name">
|
||||
Description
|
||||
</Typography>
|
||||
<Input.TextArea
|
||||
data-testid="dashboard-desc"
|
||||
rows={6}
|
||||
value={updatedDescription}
|
||||
className="description-text-area"
|
||||
onChange={(e): void => setUpdatedDescription(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<Typography style={{ marginBottom: '0.5rem' }} className="dashboard-name">
|
||||
Tags
|
||||
</Typography>
|
||||
<AddTags tags={updatedTags} setTags={setUpdatedTags} />
|
||||
</div>
|
||||
</Space>
|
||||
</Col>
|
||||
{numberOfUnsavedChanges > 0 && (
|
||||
<div className="overview-settings-footer">
|
||||
<div className="unsaved">
|
||||
<div className="unsaved-dot" />
|
||||
<Typography.Text className="unsaved-changes">
|
||||
{numberOfUnsavedChanges} Unsaved change
|
||||
</Typography.Text>
|
||||
</div>
|
||||
<div className="footer-action-btns">
|
||||
<Button
|
||||
disabled={updateDashboardMutation.isLoading}
|
||||
icon={<X size={14} />}
|
||||
onClick={discardHandler}
|
||||
type="text"
|
||||
className="discard-btn"
|
||||
>
|
||||
Discard
|
||||
</Button>
|
||||
<Button
|
||||
style={{
|
||||
margin: '16px 0',
|
||||
}}
|
||||
disabled={updateDashboardMutation.isLoading}
|
||||
loading={updateDashboardMutation.isLoading}
|
||||
icon={<Check size={14} />}
|
||||
data-testid="save-dashboard-config"
|
||||
onClick={onSaveHandler}
|
||||
type="primary"
|
||||
className="save-btn"
|
||||
>
|
||||
{t('save')}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<Typography style={{ marginBottom: '0.5rem' }}>Tags</Typography>
|
||||
<AddTags tags={updatedTags} setTags={setUpdatedTags} />
|
||||
</div>
|
||||
<div>
|
||||
<Button
|
||||
style={{
|
||||
margin: '16px 0',
|
||||
}}
|
||||
disabled={updateDashboardMutation.isLoading}
|
||||
loading={updateDashboardMutation.isLoading}
|
||||
icon={<SaveOutlined />}
|
||||
data-testid="save-dashboard-config"
|
||||
onClick={onSaveHandler}
|
||||
type="primary"
|
||||
>
|
||||
{t('save')}
|
||||
</Button>
|
||||
</div>
|
||||
</Space>
|
||||
</Col>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
File diff suppressed because one or more lines are too long
@ -6,3 +6,485 @@
|
||||
margin-bottom: 1rem;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.variable-item-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
border-radius: 3px;
|
||||
border: 1px solid var(--bg-slate-500);
|
||||
|
||||
.all-variables {
|
||||
display: flex;
|
||||
padding: 10px 16px;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
border-bottom: 1px solid var(--bg-slate-500);
|
||||
.all-variables-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 24px;
|
||||
padding: 0px;
|
||||
color: #c0c1c3;
|
||||
font-family: Inter;
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 18px; /* 128.571% */
|
||||
}
|
||||
|
||||
.all-variables-btn:hover {
|
||||
background-color: unset !important;
|
||||
}
|
||||
}
|
||||
|
||||
.variable-item-content {
|
||||
padding: 12px 16px 20px 16px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 20px;
|
||||
|
||||
.variable-name-section {
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
|
||||
.typography-variables {
|
||||
color: var(--bg-vanilla-400);
|
||||
font-family: Inter;
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 20px; /* 142.857% */
|
||||
}
|
||||
|
||||
.name-input {
|
||||
border-radius: 2px;
|
||||
border: 1px solid var(--bg-slate-400);
|
||||
background: var(--bg-ink-300);
|
||||
padding: 6px 6px 6px 8px;
|
||||
}
|
||||
}
|
||||
.variable-description-section {
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
|
||||
.typography-variables {
|
||||
color: var(--bg-vanilla-400);
|
||||
font-family: Inter;
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 20px; /* 142.857% */
|
||||
}
|
||||
|
||||
.description-input {
|
||||
border-radius: 2px;
|
||||
border: 1px solid var(--bg-slate-400);
|
||||
background: var(--bg-ink-300);
|
||||
padding: 6px 6px 6px 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.variable-type-section {
|
||||
justify-content: space-between;
|
||||
margin-bottom: 0px;
|
||||
align-items: center;
|
||||
|
||||
.typography-variables {
|
||||
color: var(--bg-vanilla-400);
|
||||
font-family: Inter;
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 20px; /* 142.857% */
|
||||
}
|
||||
|
||||
.variable-type-btn-group {
|
||||
display: flex;
|
||||
width: 342px;
|
||||
height: 32px;
|
||||
flex-shrink: 0;
|
||||
border-radius: 2px;
|
||||
border: 1px solid var(--bg-slate-400);
|
||||
background: var(--bg-ink-400);
|
||||
box-shadow: 0px 0px 8px 0px rgba(0, 0, 0, 0.1);
|
||||
|
||||
.ant-btn {
|
||||
width: 114px;
|
||||
height: 32px;
|
||||
flex-shrink: 0;
|
||||
border-radius: 2px 0px 0px 2px;
|
||||
}
|
||||
|
||||
.variable-type-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.variable-type-btn + .variable-type-btn {
|
||||
border-left: 1px solid var(--bg-slate-400);
|
||||
}
|
||||
.selected {
|
||||
background: var(--bg-slate-400);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.sort-values-section {
|
||||
justify-content: space-between;
|
||||
margin-bottom: 0;
|
||||
|
||||
.typography-variables {
|
||||
color: var(--bg-vanilla-100);
|
||||
font-family: Inter;
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 20px; /* 142.857% */
|
||||
letter-spacing: -0.07px;
|
||||
}
|
||||
|
||||
.typography-sort {
|
||||
color: var(--bg-vanilla-400);
|
||||
font-family: Inter;
|
||||
font-size: 12px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 20px; /* 166.667% */
|
||||
letter-spacing: -0.06px;
|
||||
}
|
||||
|
||||
.sort-input {
|
||||
border-radius: 2px;
|
||||
border: 1px solid var(--bg-slate-400);
|
||||
|
||||
.ant-select-selector {
|
||||
width: 192px;
|
||||
height: 32px;
|
||||
padding: 6px 6px 6px 8px;
|
||||
background: var(--bg-ink-300);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.multiple-values-section {
|
||||
justify-content: space-between;
|
||||
margin-bottom: 0;
|
||||
|
||||
.typography-variables {
|
||||
color: var(--bg-vanilla-400);
|
||||
font-family: Inter;
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 20px; /* 142.857% */
|
||||
letter-spacing: -0.07px;
|
||||
width: 339px;
|
||||
}
|
||||
}
|
||||
|
||||
.all-option-section {
|
||||
justify-content: space-between;
|
||||
margin-bottom: 0;
|
||||
|
||||
.typography-variables {
|
||||
color: var(--bg-vanilla-400);
|
||||
font-family: Inter;
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 20px; /* 142.857% */
|
||||
letter-spacing: -0.07px;
|
||||
width: 339px;
|
||||
}
|
||||
}
|
||||
|
||||
.variable-textbox-section {
|
||||
justify-content: space-between;
|
||||
margin-bottom: 0;
|
||||
align-items: center;
|
||||
|
||||
.typography-variables {
|
||||
color: var(--bg-vanilla-400);
|
||||
font-family: Inter;
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 20px; /* 142.857% */
|
||||
}
|
||||
|
||||
.default-input {
|
||||
display: flex;
|
||||
height: 32px;
|
||||
padding: 6px 6px 6px 8px;
|
||||
align-items: flex-start;
|
||||
gap: 4px;
|
||||
flex: 1 0 0;
|
||||
border-radius: 2px;
|
||||
border: 1px solid var(--bg-slate-400);
|
||||
background: var(--bg-ink-300);
|
||||
width: 342px;
|
||||
}
|
||||
}
|
||||
|
||||
.variable-custom-section {
|
||||
margin-bottom: 0px;
|
||||
.custom-collapse {
|
||||
width: 100%;
|
||||
border-radius: 3px 3px 0px 0px;
|
||||
border: 1px solid var(--bg-slate-500);
|
||||
|
||||
.ant-collapse-item {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.ant-collapse-header {
|
||||
height: 38px;
|
||||
border-radius: 3px 3px 0px 0px;
|
||||
background: var(--bg-ink-300);
|
||||
align-items: center;
|
||||
padding: 12px;
|
||||
gap: 8px;
|
||||
|
||||
.ant-collapse-expand-icon {
|
||||
padding-inline-end: 0px;
|
||||
}
|
||||
|
||||
.ant-collapse-header-text {
|
||||
color: var(--bg-robin-400);
|
||||
font-family: 'Space Mono';
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 18px; /* 128.571% */
|
||||
display: flex;
|
||||
padding: 1px 2px;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
border-radius: 2px;
|
||||
background: rgba(113, 144, 249, 0.08);
|
||||
}
|
||||
}
|
||||
|
||||
.ant-collapse-content {
|
||||
border-top: none;
|
||||
|
||||
.ant-collapse-content-box {
|
||||
padding: 0px;
|
||||
}
|
||||
|
||||
.comma-input {
|
||||
height: 109px;
|
||||
border: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.variables-preview-section {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin-bottom: 0px;
|
||||
border-radius: 0px 0px 3px 3px;
|
||||
border: 1px solid var(--bg-slate-500);
|
||||
height: 108px;
|
||||
margin-top: -20px;
|
||||
border-collapse: collapse;
|
||||
gap: 5px;
|
||||
flex-flow: column;
|
||||
|
||||
.typography-variables {
|
||||
color: var(--bg-robin-400);
|
||||
font-family: 'Space Mono';
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 18px; /* 128.571% */
|
||||
display: inline-flex;
|
||||
padding: 1px 2px;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
border-radius: 0px 0px 2px 0px;
|
||||
background: rgba(113, 144, 249, 0.08);
|
||||
}
|
||||
|
||||
.preview-values {
|
||||
padding: 4.5px 11px;
|
||||
display: flex;
|
||||
overflow-y: auto;
|
||||
flex-flow: wrap;
|
||||
gap: 8px;
|
||||
|
||||
.ant-tag {
|
||||
height: 30px;
|
||||
color: var(--bg-vanilla-100);
|
||||
font-family: 'Space Mono';
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 20px;
|
||||
display: inline-flex;
|
||||
letter-spacing: -0.07px;
|
||||
align-items: center;
|
||||
border-radius: 2px;
|
||||
border: 1px solid var(--bg-slate-300);
|
||||
margin-inline-end: 0px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.variable-item-footer {
|
||||
margin-top: 12px;
|
||||
display: flex;
|
||||
flex-direction: row-reverse;
|
||||
|
||||
.footer-btn-discard {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
border-radius: 2px;
|
||||
border: 1px solid var(--bg-slate-400);
|
||||
background: var(--bg-slate-500);
|
||||
height: 34px;
|
||||
padding: 4px 8px 4px 10px;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.footer-btn-save {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
border-radius: 2px;
|
||||
border: 1px solid var(--bg-robin-500);
|
||||
background: var(--bg-robin-500);
|
||||
width: 123px;
|
||||
height: 34px;
|
||||
padding: 4px 8px 4px 10px;
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
|
||||
.lightMode {
|
||||
.variable-item-container {
|
||||
border: 1px solid var(--bg-vanilla-300);
|
||||
|
||||
.all-variables {
|
||||
border-bottom: 1px solid var(--bg-vanilla-300);
|
||||
.all-variables-btn {
|
||||
color: var(--bg-ink-300);
|
||||
}
|
||||
}
|
||||
|
||||
.variable-item-content {
|
||||
.variable-name-section {
|
||||
.typography-variables {
|
||||
color: var(--bg-ink-400);
|
||||
}
|
||||
|
||||
.name-input {
|
||||
border: 1px solid var(--bg-vanilla-300);
|
||||
background: var(--bg-vanilla-300);
|
||||
}
|
||||
}
|
||||
.variable-description-section {
|
||||
.typography-variables {
|
||||
color: var(--bg-ink-400);
|
||||
}
|
||||
|
||||
.description-input {
|
||||
border: 1px solid var(--bg-vanilla-300);
|
||||
background: var(--bg-vanilla-300);
|
||||
}
|
||||
}
|
||||
|
||||
.variable-type-section {
|
||||
.typography-variables {
|
||||
color: var(--bg-slate-400);
|
||||
}
|
||||
|
||||
.variable-type-btn-group {
|
||||
border: 1px solid var(--bg-vanilla-300);
|
||||
background: var(--bg-vanilla-300);
|
||||
|
||||
.variable-type-btn + .variable-type-btn {
|
||||
border-left: 1px solid var(--bg-vanilla-200);
|
||||
}
|
||||
.selected {
|
||||
background: var(--bg-vanilla-200);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.sort-values-section {
|
||||
.typography-variables {
|
||||
color: var(--bg-ink-300);
|
||||
}
|
||||
|
||||
.typography-sort {
|
||||
color: var(--bg-ink-400);
|
||||
}
|
||||
|
||||
.sort-input {
|
||||
border: 1px solid var(--bg-vanilla-300);
|
||||
|
||||
.ant-select-selector {
|
||||
background: var(--bg-vanilla-300);
|
||||
border: var(--bg-vanilla-300);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.multiple-values-section {
|
||||
.typography-variables {
|
||||
color: var(--bg-ink-400);
|
||||
}
|
||||
}
|
||||
|
||||
.all-option-section {
|
||||
.typography-variables {
|
||||
color: var(--bg-ink-400);
|
||||
}
|
||||
}
|
||||
|
||||
.variable-textbox-section {
|
||||
.typography-variables {
|
||||
color: var(--bg-ink-400);
|
||||
}
|
||||
|
||||
.default-input {
|
||||
border: 1px solid var(--bg-vanilla-300);
|
||||
background: var(--bg-vanilla-300);
|
||||
color: var(--bg-ink-300);
|
||||
}
|
||||
}
|
||||
|
||||
.variable-custom-section {
|
||||
.custom-collapse {
|
||||
border: 1px solid var(--bg-vanilla-300);
|
||||
|
||||
.ant-collapse-header {
|
||||
background: var(--bg-vanilla-300);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.variables-preview-section {
|
||||
border: 1px solid var(--bg-vanilla-300);
|
||||
|
||||
.preview-values {
|
||||
.ant-tag {
|
||||
color: var(--bg-slate-300);
|
||||
border: 1px solid var(--bg-vanilla-300);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.variable-item-footer {
|
||||
.footer-btn-discard {
|
||||
border: 1px solid var(--bg-vanilla-300);
|
||||
background: var(--bg-vanilla-200);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,20 +2,28 @@
|
||||
import './VariableItem.styles.scss';
|
||||
|
||||
import { orange } from '@ant-design/colors';
|
||||
import { Button, Divider, Input, Select, Switch, Tag, Typography } from 'antd';
|
||||
import { Button, Collapse, Input, Select, Switch, Tag, Typography } from 'antd';
|
||||
import dashboardVariablesQuery from 'api/dashboard/variables/dashboardVariablesQuery';
|
||||
import cx from 'classnames';
|
||||
import Editor from 'components/Editor';
|
||||
import { REACT_QUERY_KEY } from 'constants/reactQueryKeys';
|
||||
import { commaValuesParser } from 'lib/dashbaordVariables/customCommaValuesParser';
|
||||
import sortValues from 'lib/dashbaordVariables/sortVariableValues';
|
||||
import { map } from 'lodash-es';
|
||||
import {
|
||||
ArrowLeft,
|
||||
Check,
|
||||
ClipboardType,
|
||||
DatabaseZap,
|
||||
LayoutList,
|
||||
X,
|
||||
} from 'lucide-react';
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
import { useQuery } from 'react-query';
|
||||
import {
|
||||
IDashboardVariable,
|
||||
TSortVariableValuesType,
|
||||
TVariableQueryType,
|
||||
VariableQueryTypeArr,
|
||||
VariableSortTypeArr,
|
||||
} from 'types/api/dashboard/getAll';
|
||||
import { v4 as generateUUID } from 'uuid';
|
||||
@ -79,7 +87,6 @@ function VariableItem({
|
||||
const [errorPreview, setErrorPreview] = useState<string | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
setPreviewValues([]);
|
||||
if (queryType === 'CUSTOM') {
|
||||
setPreviewValues(
|
||||
sortValues(
|
||||
@ -88,6 +95,9 @@ function VariableItem({
|
||||
) as never,
|
||||
);
|
||||
}
|
||||
if (queryType === 'QUERY') {
|
||||
setPreviewValues((prev) => sortValues(prev, variableSortType) as never);
|
||||
}
|
||||
}, [
|
||||
queryType,
|
||||
variableCustomValue,
|
||||
@ -121,13 +131,16 @@ function VariableItem({
|
||||
|
||||
// Fetches the preview values for the SQL variable query
|
||||
const handleQueryResult = (response: any): void => {
|
||||
if (response?.payload?.variableValues)
|
||||
if (response?.payload?.variableValues) {
|
||||
setPreviewValues(
|
||||
sortValues(
|
||||
response.payload?.variableValues || [],
|
||||
variableSortType,
|
||||
) as never,
|
||||
);
|
||||
} else {
|
||||
setPreviewValues([]);
|
||||
}
|
||||
};
|
||||
|
||||
const { isFetching: previewLoading, refetch: runQuery } = useQuery(
|
||||
@ -169,219 +182,288 @@ function VariableItem({
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="variable-item-container">
|
||||
<div className="variable-item-content">
|
||||
<VariableItemRow>
|
||||
<LabelContainer>
|
||||
<Typography>Name</Typography>
|
||||
</LabelContainer>
|
||||
<div>
|
||||
<Input
|
||||
placeholder="Unique name of the variable"
|
||||
style={{ width: 400 }}
|
||||
value={variableName}
|
||||
onChange={(e): void => {
|
||||
setVariableName(e.target.value);
|
||||
setErrorName(
|
||||
!validateName(e.target.value) && e.target.value !== variableData.name,
|
||||
);
|
||||
}}
|
||||
/>
|
||||
<div>
|
||||
<Typography.Text type="warning">
|
||||
{errorName ? 'Variable name already exists' : ''}
|
||||
</Typography.Text>
|
||||
</div>
|
||||
</div>
|
||||
</VariableItemRow>
|
||||
<VariableItemRow>
|
||||
<LabelContainer>
|
||||
<Typography>Description</Typography>
|
||||
</LabelContainer>
|
||||
|
||||
<Input.TextArea
|
||||
value={variableDescription}
|
||||
placeholder="Write description of the variable"
|
||||
style={{ width: 400 }}
|
||||
onChange={(e): void => setVariableDescription(e.target.value)}
|
||||
/>
|
||||
</VariableItemRow>
|
||||
<VariableItemRow>
|
||||
<LabelContainer>
|
||||
<Typography>Type</Typography>
|
||||
</LabelContainer>
|
||||
|
||||
<Select
|
||||
defaultActiveFirstOption
|
||||
style={{ width: 400 }}
|
||||
onChange={(e: TVariableQueryType): void => {
|
||||
setQueryType(e);
|
||||
}}
|
||||
value={queryType}
|
||||
<>
|
||||
<div className="variable-item-container">
|
||||
<div className="all-variables">
|
||||
<Button
|
||||
type="text"
|
||||
className="all-variables-btn"
|
||||
icon={<ArrowLeft size={14} />}
|
||||
onClick={onCancel}
|
||||
>
|
||||
<Option value={VariableQueryTypeArr[0]}>Query</Option>
|
||||
<Option value={VariableQueryTypeArr[1]}>Textbox</Option>
|
||||
<Option value={VariableQueryTypeArr[2]}>Custom</Option>
|
||||
</Select>
|
||||
</VariableItemRow>
|
||||
<Typography.Title
|
||||
level={5}
|
||||
style={{ marginTop: '1rem', marginBottom: '1rem' }}
|
||||
>
|
||||
Options
|
||||
</Typography.Title>
|
||||
{queryType === 'QUERY' && (
|
||||
<div className="query-container">
|
||||
All variables
|
||||
</Button>
|
||||
</div>
|
||||
<div className="variable-item-content">
|
||||
<VariableItemRow className="variable-name-section">
|
||||
<LabelContainer>
|
||||
<Typography>Query</Typography>
|
||||
<Typography className="typography-variables">Name</Typography>
|
||||
</LabelContainer>
|
||||
|
||||
<div style={{ flex: 1, position: 'relative' }}>
|
||||
<Editor
|
||||
language="sql"
|
||||
value={variableQueryValue}
|
||||
onChange={(e): void => setVariableQueryValue(e)}
|
||||
height="240px"
|
||||
options={{
|
||||
fontSize: 13,
|
||||
wordWrap: 'on',
|
||||
lineNumbers: 'off',
|
||||
glyphMargin: false,
|
||||
folding: false,
|
||||
lineDecorationsWidth: 0,
|
||||
lineNumbersMinChars: 0,
|
||||
minimap: {
|
||||
enabled: false,
|
||||
},
|
||||
<div>
|
||||
<Input
|
||||
placeholder="Unique name of the variable"
|
||||
value={variableName}
|
||||
className="name-input"
|
||||
onChange={(e): void => {
|
||||
setVariableName(e.target.value);
|
||||
setErrorName(
|
||||
!validateName(e.target.value) && e.target.value !== variableData.name,
|
||||
);
|
||||
}}
|
||||
/>
|
||||
<div>
|
||||
<Typography.Text type="warning">
|
||||
{errorName ? 'Variable name already exists' : ''}
|
||||
</Typography.Text>
|
||||
</div>
|
||||
</div>
|
||||
</VariableItemRow>
|
||||
<VariableItemRow className="variable-description-section">
|
||||
<LabelContainer>
|
||||
<Typography className="typography-variables">Description</Typography>
|
||||
</LabelContainer>
|
||||
|
||||
<Input.TextArea
|
||||
value={variableDescription}
|
||||
placeholder="Enter a description for the variable"
|
||||
className="description-input"
|
||||
rows={3}
|
||||
onChange={(e): void => setVariableDescription(e.target.value)}
|
||||
/>
|
||||
</VariableItemRow>
|
||||
<VariableItemRow className="variable-type-section">
|
||||
<LabelContainer>
|
||||
<Typography className="typography-variables">Variable Type</Typography>
|
||||
</LabelContainer>
|
||||
|
||||
<div className="variable-type-btn-group">
|
||||
<Button
|
||||
type="primary"
|
||||
size="small"
|
||||
onClick={handleTestRunQuery}
|
||||
style={{
|
||||
position: 'absolute',
|
||||
bottom: 0,
|
||||
type="text"
|
||||
icon={<DatabaseZap size={14} />}
|
||||
className={cx(
|
||||
// eslint-disable-next-line sonarjs/no-duplicate-string
|
||||
'variable-type-btn',
|
||||
queryType === 'QUERY' ? 'selected' : '',
|
||||
)}
|
||||
onClick={(): void => {
|
||||
setQueryType('QUERY');
|
||||
setPreviewValues([]);
|
||||
}}
|
||||
loading={previewLoading}
|
||||
>
|
||||
Test Run Query
|
||||
Query
|
||||
</Button>
|
||||
<Button
|
||||
type="text"
|
||||
icon={<ClipboardType size={14} />}
|
||||
className={cx(
|
||||
'variable-type-btn',
|
||||
queryType === 'TEXTBOX' ? 'selected' : '',
|
||||
)}
|
||||
onClick={(): void => {
|
||||
setQueryType('TEXTBOX');
|
||||
setPreviewValues([]);
|
||||
}}
|
||||
>
|
||||
Textbox
|
||||
</Button>
|
||||
<Button
|
||||
type="text"
|
||||
icon={<LayoutList size={14} />}
|
||||
className={cx(
|
||||
'variable-type-btn',
|
||||
queryType === 'CUSTOM' ? 'selected' : '',
|
||||
)}
|
||||
onClick={(): void => {
|
||||
setQueryType('CUSTOM');
|
||||
setPreviewValues([]);
|
||||
}}
|
||||
>
|
||||
Custom
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{queryType === 'CUSTOM' && (
|
||||
<VariableItemRow>
|
||||
<LabelContainer>
|
||||
<Typography>Values separated by comma</Typography>
|
||||
</LabelContainer>
|
||||
<Input.TextArea
|
||||
value={variableCustomValue}
|
||||
placeholder="1, 10, mykey, mykey:myvalue"
|
||||
style={{ width: 400 }}
|
||||
onChange={(e): void => {
|
||||
setVariableCustomValue(e.target.value);
|
||||
setPreviewValues(
|
||||
sortValues(
|
||||
commaValuesParser(e.target.value),
|
||||
variableSortType,
|
||||
) as never,
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</VariableItemRow>
|
||||
)}
|
||||
{queryType === 'TEXTBOX' && (
|
||||
<VariableItemRow>
|
||||
<LabelContainer>
|
||||
<Typography>Default Value</Typography>
|
||||
</LabelContainer>
|
||||
<Input
|
||||
value={variableTextboxValue}
|
||||
onChange={(e): void => {
|
||||
setVariableTextboxValue(e.target.value);
|
||||
}}
|
||||
placeholder="Default value if any"
|
||||
style={{ width: 400 }}
|
||||
/>
|
||||
</VariableItemRow>
|
||||
)}
|
||||
{(queryType === 'QUERY' || queryType === 'CUSTOM') && (
|
||||
<>
|
||||
<VariableItemRow>
|
||||
{queryType === 'QUERY' && (
|
||||
<div className="query-container">
|
||||
<LabelContainer>
|
||||
<Typography>Preview of Values</Typography>
|
||||
</LabelContainer>
|
||||
<div style={{ flex: 1 }}>
|
||||
{errorPreview ? (
|
||||
<Typography style={{ color: orange[5] }}>{errorPreview}</Typography>
|
||||
) : (
|
||||
map(previewValues, (value, idx) => (
|
||||
<Tag key={`${value}${idx}`}>{value.toString()}</Tag>
|
||||
))
|
||||
)}
|
||||
</div>
|
||||
</VariableItemRow>
|
||||
<VariableItemRow>
|
||||
<LabelContainer>
|
||||
<Typography>Sort</Typography>
|
||||
<Typography>Query</Typography>
|
||||
</LabelContainer>
|
||||
|
||||
<Select
|
||||
defaultActiveFirstOption
|
||||
style={{ width: 400 }}
|
||||
defaultValue={VariableSortTypeArr[0]}
|
||||
value={variableSortType}
|
||||
onChange={(value: TSortVariableValuesType): void =>
|
||||
setVariableSortType(value)
|
||||
}
|
||||
>
|
||||
<Option value={VariableSortTypeArr[0]}>Disabled</Option>
|
||||
<Option value={VariableSortTypeArr[1]}>Ascending</Option>
|
||||
<Option value={VariableSortTypeArr[2]}>Descending</Option>
|
||||
</Select>
|
||||
</VariableItemRow>
|
||||
<VariableItemRow>
|
||||
<LabelContainer>
|
||||
<Typography>Enable multiple values to be checked</Typography>
|
||||
</LabelContainer>
|
||||
<Switch
|
||||
checked={variableMultiSelect}
|
||||
onChange={(e): void => {
|
||||
setVariableMultiSelect(e);
|
||||
if (!e) {
|
||||
setVariableShowALLOption(false);
|
||||
}
|
||||
}}
|
||||
<div style={{ flex: 1, position: 'relative' }}>
|
||||
<Editor
|
||||
language="sql"
|
||||
value={variableQueryValue}
|
||||
onChange={(e): void => setVariableQueryValue(e)}
|
||||
height="240px"
|
||||
options={{
|
||||
fontSize: 13,
|
||||
wordWrap: 'on',
|
||||
lineNumbers: 'off',
|
||||
glyphMargin: false,
|
||||
folding: false,
|
||||
lineDecorationsWidth: 0,
|
||||
lineNumbersMinChars: 0,
|
||||
minimap: {
|
||||
enabled: false,
|
||||
},
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
type="primary"
|
||||
size="small"
|
||||
onClick={handleTestRunQuery}
|
||||
style={{
|
||||
position: 'absolute',
|
||||
bottom: 0,
|
||||
}}
|
||||
loading={previewLoading}
|
||||
>
|
||||
Test Run Query
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{queryType === 'CUSTOM' && (
|
||||
<VariableItemRow className="variable-custom-section">
|
||||
<Collapse
|
||||
collapsible="header"
|
||||
rootClassName="custom-collapse"
|
||||
defaultActiveKey={['1']}
|
||||
items={[
|
||||
{
|
||||
key: '1',
|
||||
label: 'Options',
|
||||
children: (
|
||||
<Input.TextArea
|
||||
value={variableCustomValue}
|
||||
placeholder="Enter options separated by commas."
|
||||
rootClassName="comma-input"
|
||||
onChange={(e): void => {
|
||||
setVariableCustomValue(e.target.value);
|
||||
setPreviewValues(
|
||||
sortValues(
|
||||
commaValuesParser(e.target.value),
|
||||
variableSortType,
|
||||
) as never,
|
||||
);
|
||||
}}
|
||||
/>
|
||||
),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</VariableItemRow>
|
||||
{variableMultiSelect && (
|
||||
<VariableItemRow>
|
||||
)}
|
||||
{queryType === 'TEXTBOX' && (
|
||||
<VariableItemRow className="variable-textbox-section">
|
||||
<LabelContainer>
|
||||
<Typography className="typography-variables">Default Value</Typography>
|
||||
</LabelContainer>
|
||||
<Input
|
||||
value={variableTextboxValue}
|
||||
className="default-input"
|
||||
onChange={(e): void => {
|
||||
setVariableTextboxValue(e.target.value);
|
||||
}}
|
||||
placeholder="Enter a default value (if any)..."
|
||||
style={{ width: 400 }}
|
||||
/>
|
||||
</VariableItemRow>
|
||||
)}
|
||||
{(queryType === 'QUERY' || queryType === 'CUSTOM') && (
|
||||
<>
|
||||
<VariableItemRow className="variables-preview-section">
|
||||
<LabelContainer style={{ width: '100%' }}>
|
||||
<Typography className="typography-variables">
|
||||
Preview of Values
|
||||
</Typography>
|
||||
</LabelContainer>
|
||||
<div className="preview-values">
|
||||
{errorPreview ? (
|
||||
<Typography style={{ color: orange[5] }}>{errorPreview}</Typography>
|
||||
) : (
|
||||
map(previewValues, (value, idx) => (
|
||||
<Tag key={`${value}${idx}`}>{value.toString()}</Tag>
|
||||
))
|
||||
)}
|
||||
</div>
|
||||
</VariableItemRow>
|
||||
<VariableItemRow className="sort-values-section">
|
||||
<LabelContainer>
|
||||
<Typography>Include an option for ALL values</Typography>
|
||||
<Typography className="typography-variables">Sort Values</Typography>
|
||||
<Typography className="typography-sort">
|
||||
Sort the query output values
|
||||
</Typography>
|
||||
</LabelContainer>
|
||||
|
||||
<Select
|
||||
defaultActiveFirstOption
|
||||
defaultValue={VariableSortTypeArr[0]}
|
||||
value={variableSortType}
|
||||
onChange={(value: TSortVariableValuesType): void =>
|
||||
setVariableSortType(value)
|
||||
}
|
||||
className="sort-input"
|
||||
>
|
||||
<Option value={VariableSortTypeArr[0]}>Disabled</Option>
|
||||
<Option value={VariableSortTypeArr[1]}>Ascending</Option>
|
||||
<Option value={VariableSortTypeArr[2]}>Descending</Option>
|
||||
</Select>
|
||||
</VariableItemRow>
|
||||
<VariableItemRow className="multiple-values-section">
|
||||
<LabelContainer>
|
||||
<Typography className="typography-variables">
|
||||
Enable multiple values to be checked
|
||||
</Typography>
|
||||
</LabelContainer>
|
||||
<Switch
|
||||
checked={variableShowALLOption}
|
||||
onChange={(e): void => setVariableShowALLOption(e)}
|
||||
checked={variableMultiSelect}
|
||||
onChange={(e): void => {
|
||||
setVariableMultiSelect(e);
|
||||
if (!e) {
|
||||
setVariableShowALLOption(false);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</VariableItemRow>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
{variableMultiSelect && (
|
||||
<VariableItemRow className="all-option-section">
|
||||
<LabelContainer>
|
||||
<Typography className="typography-variables">
|
||||
Include an option for ALL values
|
||||
</Typography>
|
||||
</LabelContainer>
|
||||
<Switch
|
||||
checked={variableShowALLOption}
|
||||
onChange={(e): void => setVariableShowALLOption(e)}
|
||||
/>
|
||||
</VariableItemRow>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="variable-item-footer">
|
||||
<Divider />
|
||||
<VariableItemRow>
|
||||
<Button type="primary" onClick={handleSave} disabled={errorName}>
|
||||
Save
|
||||
<Button
|
||||
type="default"
|
||||
onClick={onCancel}
|
||||
icon={<X size={14} />}
|
||||
className="footer-btn-discard"
|
||||
>
|
||||
Discard
|
||||
</Button>
|
||||
<Button type="default" onClick={onCancel}>
|
||||
Cancel
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={handleSave}
|
||||
disabled={errorName}
|
||||
icon={<Check size={14} />}
|
||||
className="footer-btn-save"
|
||||
>
|
||||
Save Variable
|
||||
</Button>
|
||||
</VariableItemRow>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,6 @@
|
||||
import '../DashboardSettings.styles.scss';
|
||||
|
||||
import { blue, red } from '@ant-design/colors';
|
||||
import { MenuOutlined, PlusOutlined } from '@ant-design/icons';
|
||||
import { HolderOutlined, PlusOutlined } from '@ant-design/icons';
|
||||
import type { DragEndEvent, UniqueIdentifier } from '@dnd-kit/core';
|
||||
import {
|
||||
DndContext,
|
||||
@ -18,7 +17,7 @@ import { RowProps } from 'antd/lib';
|
||||
import { convertVariablesToDbFormat } from 'container/NewDashboard/DashboardVariablesSelection/util';
|
||||
import { useUpdateDashboard } from 'hooks/dashboard/useUpdateDashboard';
|
||||
import { useNotifications } from 'hooks/useNotifications';
|
||||
import { PencilIcon, TrashIcon } from 'lucide-react';
|
||||
import { PenLine, Trash2 } from 'lucide-react';
|
||||
import { useDashboard } from 'providers/Dashboard/Dashboard';
|
||||
import React, { useEffect, useRef, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
@ -53,25 +52,33 @@ function TableRow({ children, ...props }: RowProps): JSX.Element {
|
||||
// eslint-disable-next-line react/jsx-props-no-spreading
|
||||
<tr {...props} ref={setNodeRef} style={style} {...attributes}>
|
||||
{React.Children.map(children, (child) => {
|
||||
if ((child as React.ReactElement).key === 'sort') {
|
||||
if ((child as React.ReactElement).key === 'name') {
|
||||
return React.cloneElement(child as React.ReactElement, {
|
||||
children: (
|
||||
<MenuOutlined
|
||||
ref={setActivatorNodeRef}
|
||||
style={{ touchAction: 'none', cursor: 'move' }}
|
||||
// eslint-disable-next-line react/jsx-props-no-spreading
|
||||
{...listeners}
|
||||
/>
|
||||
<div className="variable-name-drag">
|
||||
<HolderOutlined
|
||||
ref={setActivatorNodeRef}
|
||||
style={{ touchAction: 'none', cursor: 'move' }}
|
||||
// eslint-disable-next-line react/jsx-props-no-spreading
|
||||
{...listeners}
|
||||
/>
|
||||
{child}
|
||||
</div>
|
||||
),
|
||||
});
|
||||
}
|
||||
|
||||
return child;
|
||||
})}
|
||||
</tr>
|
||||
);
|
||||
}
|
||||
|
||||
function VariablesSetting(): JSX.Element {
|
||||
function VariablesSetting({
|
||||
variableViewModeRef,
|
||||
}: {
|
||||
variableViewModeRef: React.MutableRefObject<(() => void) | undefined>;
|
||||
}): JSX.Element {
|
||||
const variableToDelete = useRef<IDashboardVariable | null>(null);
|
||||
const [deleteVariableModal, setDeleteVariableModal] = useState(false);
|
||||
|
||||
@ -111,6 +118,13 @@ function VariablesSetting(): JSX.Element {
|
||||
setVariableViewMode(viewType);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (variableViewModeRef) {
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
variableViewModeRef.current = onDoneVariableViewMode;
|
||||
}
|
||||
}, [variableViewModeRef]);
|
||||
|
||||
const updateMutation = useUpdateDashboard();
|
||||
|
||||
useEffect(() => {
|
||||
@ -245,47 +259,42 @@ function VariablesSetting(): JSX.Element {
|
||||
!existingVariableNamesMap[name];
|
||||
|
||||
const columns = [
|
||||
{
|
||||
key: 'sort',
|
||||
width: '10%',
|
||||
},
|
||||
{
|
||||
title: 'Variable',
|
||||
dataIndex: 'name',
|
||||
width: '40%',
|
||||
width: '50%',
|
||||
key: 'name',
|
||||
},
|
||||
{
|
||||
title: 'Description',
|
||||
dataIndex: 'description',
|
||||
width: '35%',
|
||||
width: '50%',
|
||||
key: 'description',
|
||||
},
|
||||
{
|
||||
title: 'Actions',
|
||||
width: '15%',
|
||||
key: 'action',
|
||||
render: (variable: IDashboardVariable): JSX.Element => (
|
||||
<Space>
|
||||
<Button
|
||||
type="text"
|
||||
style={{ padding: 8, cursor: 'pointer', color: blue[5] }}
|
||||
onClick={(): void => onVariableViewModeEnter('EDIT', variable)}
|
||||
>
|
||||
<PencilIcon size={14} />
|
||||
</Button>
|
||||
<Button
|
||||
type="text"
|
||||
style={{ padding: 8, color: red[6], cursor: 'pointer' }}
|
||||
onClick={(): void => {
|
||||
if (variable) {
|
||||
onVariableDeleteHandler(variable);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<TrashIcon size={14} />
|
||||
</Button>
|
||||
</Space>
|
||||
<div className="variable-description-actions">
|
||||
<Typography.Text className="variable-description">
|
||||
{variable.description}
|
||||
</Typography.Text>
|
||||
<Space className="actions-btns">
|
||||
<Button
|
||||
type="text"
|
||||
onClick={(): void => onVariableViewModeEnter('EDIT', variable)}
|
||||
className="edit-variable-button"
|
||||
>
|
||||
<PenLine size={14} />
|
||||
</Button>
|
||||
<Button
|
||||
type="text"
|
||||
onClick={(): void => {
|
||||
if (variable) {
|
||||
onVariableDeleteHandler(variable);
|
||||
}
|
||||
}}
|
||||
className="delete-variable-button"
|
||||
>
|
||||
<Trash2 size={14} />
|
||||
</Button>
|
||||
</Space>
|
||||
</div>
|
||||
),
|
||||
},
|
||||
];
|
||||
@ -353,6 +362,10 @@ function VariablesSetting(): JSX.Element {
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'flex-end',
|
||||
padding: '0.5rem 0',
|
||||
position: 'absolute',
|
||||
top: '-56px',
|
||||
right: '0px',
|
||||
zIndex: '1',
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
@ -385,6 +398,7 @@ function VariablesSetting(): JSX.Element {
|
||||
columns={columns}
|
||||
pagination={false}
|
||||
dataSource={variablesTableData}
|
||||
className="dashboard-variable-settings-table"
|
||||
/>
|
||||
</SortableContext>
|
||||
</DndContext>
|
||||
|
@ -1,19 +1,38 @@
|
||||
import { Tabs } from 'antd';
|
||||
import './DashboardSettingsContent.styles.scss';
|
||||
|
||||
import { Button, Tabs } from 'antd';
|
||||
import { Braces, Table } from 'lucide-react';
|
||||
|
||||
import GeneralDashboardSettings from './General';
|
||||
import VariablesSetting from './Variables';
|
||||
|
||||
function DashboardSettingsContent(): JSX.Element {
|
||||
function DashboardSettingsContent({
|
||||
variableViewModeRef,
|
||||
}: {
|
||||
variableViewModeRef: React.MutableRefObject<(() => void) | undefined>;
|
||||
}): JSX.Element {
|
||||
const items = [
|
||||
{
|
||||
label: 'General',
|
||||
label: (
|
||||
<Button type="text" icon={<Table size="14" />} className="overview-btn">
|
||||
Overview
|
||||
</Button>
|
||||
),
|
||||
key: 'general',
|
||||
children: <GeneralDashboardSettings />,
|
||||
},
|
||||
{ label: 'Variables', key: 'variables', children: <VariablesSetting /> },
|
||||
{
|
||||
label: (
|
||||
<Button type="text" icon={<Braces size={14} />} className="variables-btn">
|
||||
Variables
|
||||
</Button>
|
||||
),
|
||||
key: 'variables',
|
||||
children: <VariablesSetting variableViewModeRef={variableViewModeRef} />,
|
||||
},
|
||||
];
|
||||
|
||||
return <Tabs items={items} animated />;
|
||||
return <Tabs items={items} animated className="settings-tabs" />;
|
||||
}
|
||||
|
||||
export default DashboardSettingsContent;
|
||||
|
@ -1,16 +1,63 @@
|
||||
.variable-name {
|
||||
font-size: 0.8rem;
|
||||
min-width: 100px;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
color: gray;
|
||||
}
|
||||
|
||||
.variable-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.variable-name {
|
||||
display: flex;
|
||||
min-width: 56px;
|
||||
height: 32px;
|
||||
padding: 6px 6px 6px 8px;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
border-radius: 2px 0px 0px 2px;
|
||||
border: 1px solid var(--bg-slate-400);
|
||||
background: var(--bg-ink-300);
|
||||
color: var(--bg-robin-300);
|
||||
font-family: Inter;
|
||||
font-size: 12px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 16px; /* 133.333% */
|
||||
}
|
||||
|
||||
.variable-value {
|
||||
display: flex;
|
||||
min-width: 120px;
|
||||
height: 32px;
|
||||
padding: 6px 6px 6px 8px;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
border-radius: 0px 2px 2px 0px;
|
||||
border: 1px solid var(--bg-slate-400);
|
||||
border-left: none;
|
||||
background: var(--bg-ink-400);
|
||||
color: var(--bg-vanilla-400);
|
||||
font-family: Inter;
|
||||
font-size: 12px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 16px; /* 133.333% */
|
||||
}
|
||||
|
||||
.variable-select {
|
||||
.ant-select-dropdown {
|
||||
max-width: 300px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.lightMode {
|
||||
.variable-item {
|
||||
.variable-name {
|
||||
border: 1px solid var(--bg-vanilla-300);
|
||||
background: var(--bg-vanilla-100);
|
||||
color: var(--bg-robin-300);
|
||||
}
|
||||
|
||||
.variable-value {
|
||||
border: 1px solid var(--bg-vanilla-300);
|
||||
background: var(--bg-vanilla-100);
|
||||
color: var(--bg-ink-400);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -124,7 +124,7 @@ function DashboardVariableSelection(): JSX.Element | null {
|
||||
);
|
||||
|
||||
return (
|
||||
<Row>
|
||||
<Row style={{ display: 'flex', gap: '12px' }}>
|
||||
{orderBasedSortedVariables &&
|
||||
Array.isArray(orderBasedSortedVariables) &&
|
||||
orderBasedSortedVariables.length > 0 &&
|
||||
|
@ -16,7 +16,7 @@ import { VariableResponseProps } from 'types/api/dashboard/variables/query';
|
||||
import { popupContainer } from 'utils/selectPopupContainer';
|
||||
|
||||
import { variablePropsToPayloadVariables } from '../utils';
|
||||
import { SelectItemStyle, VariableContainer, VariableValue } from './styles';
|
||||
import { SelectItemStyle } from './styles';
|
||||
import { areArraysEqual } from './util';
|
||||
|
||||
const ALL_SELECT_VALUE = '__ALL__';
|
||||
@ -214,11 +214,11 @@ function VariableItem({
|
||||
}, [variableData.type, variableData.customValue]);
|
||||
|
||||
return (
|
||||
<VariableContainer className="variable-item">
|
||||
<div className="variable-item">
|
||||
<Typography.Text className="variable-name" ellipsis>
|
||||
${variableData.name}
|
||||
</Typography.Text>
|
||||
<VariableValue>
|
||||
<div className="variable-value">
|
||||
{variableData.type === 'TEXTBOX' ? (
|
||||
<Input
|
||||
placeholder="Enter value"
|
||||
@ -283,8 +283,8 @@ function VariableItem({
|
||||
</Popover>
|
||||
</span>
|
||||
)}
|
||||
</VariableValue>
|
||||
</VariableContainer>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -1,17 +1,17 @@
|
||||
import GridGraphLayout from 'container/GridCardLayout';
|
||||
import ComponentsSlider from 'container/NewDashboard/ComponentsSlider';
|
||||
import { useDashboard } from 'providers/Dashboard/Dashboard';
|
||||
import { FullScreenHandle } from 'react-full-screen';
|
||||
|
||||
import { GridComponentSliderContainer } from './styles';
|
||||
|
||||
function GridGraphs(): JSX.Element {
|
||||
const { isDashboardSliderOpen } = useDashboard();
|
||||
interface GridGraphsProps {
|
||||
handle: FullScreenHandle;
|
||||
}
|
||||
|
||||
function GridGraphs(props: GridGraphsProps): JSX.Element {
|
||||
const { handle } = props;
|
||||
return (
|
||||
<GridComponentSliderContainer>
|
||||
{isDashboardSliderOpen && <ComponentsSlider />}
|
||||
|
||||
<GridGraphLayout />
|
||||
<GridGraphLayout handle={handle} />
|
||||
</GridComponentSliderContainer>
|
||||
);
|
||||
}
|
||||
|
@ -1,12 +1,15 @@
|
||||
import { useFullScreenHandle } from 'react-full-screen';
|
||||
|
||||
import Description from './DashboardDescription';
|
||||
import GridGraphs from './GridGraphs';
|
||||
|
||||
function NewDashboard(): JSX.Element {
|
||||
const handle = useFullScreenHandle();
|
||||
return (
|
||||
<>
|
||||
<Description />
|
||||
<GridGraphs />
|
||||
</>
|
||||
<div style={{ overflowX: 'hidden' }}>
|
||||
<Description handle={handle} />
|
||||
<GridGraphs handle={handle} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -1,184 +1,184 @@
|
||||
.explorer-columns-renderer {
|
||||
margin-top: 10px;
|
||||
margin-top: 10px;
|
||||
margin-bottom: 30px;
|
||||
|
||||
.title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
}
|
||||
.title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
padding-left: 16px;
|
||||
}
|
||||
|
||||
.ant-typography {
|
||||
color: var(rgba(255, 255, 255, 0.85));
|
||||
font-family: "Inter";
|
||||
font-size: 13px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 22px;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
.ant-typography {
|
||||
color: var(rgba(255, 255, 255, 0.85));
|
||||
font-family: 'Inter';
|
||||
font-size: 13px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 22px;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
|
||||
.ant-divider {
|
||||
margin: 8px 0 !important;
|
||||
border: 0.5px solid var(--bg-slate-400);
|
||||
}
|
||||
.ant-divider {
|
||||
margin: 8px 0 !important;
|
||||
border: 0.5px solid var(--bg-slate-400);
|
||||
}
|
||||
|
||||
.explorer-columns-contents {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
.explorer-columns-contents {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding-left: 16px;
|
||||
padding-right: 8px;
|
||||
|
||||
.explorer-columns {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
overflow-x: scroll;
|
||||
min-width: 90%;
|
||||
.explorer-columns {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
overflow-x: scroll;
|
||||
min-width: 90%;
|
||||
|
||||
.explorer-columns-list {
|
||||
display: flex !important;
|
||||
}
|
||||
|
||||
.explorer-column-card {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 4px;
|
||||
min-width: 200px;
|
||||
border-radius: 2px;
|
||||
border: 1px solid var(--colorBorder, rgba(118, 136, 201, 0.12));
|
||||
background: var(--bg-slate-500);
|
||||
cursor: unset;
|
||||
.explorer-columns-list {
|
||||
display: flex !important;
|
||||
}
|
||||
|
||||
.explorer-column-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
font-family: Inter;
|
||||
font-size: 12px;
|
||||
cursor: grab;
|
||||
}
|
||||
|
||||
.lucide-trash2 {
|
||||
cursor: pointer !important;
|
||||
}
|
||||
.explorer-column-card {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 4px;
|
||||
min-width: 200px;
|
||||
border-radius: 2px;
|
||||
border: 1px solid var(--colorBorder, rgba(118, 136, 201, 0.12));
|
||||
background: var(--bg-slate-500);
|
||||
cursor: unset;
|
||||
|
||||
}
|
||||
}
|
||||
.explorer-column-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
font-family: Inter;
|
||||
font-size: 12px;
|
||||
cursor: grab;
|
||||
}
|
||||
|
||||
.explorer-columns::-webkit-scrollbar {
|
||||
height: 0px; /* Height of the scrollbar */
|
||||
}
|
||||
.lucide-trash2 {
|
||||
cursor: pointer !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.action-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
padding: 0px 16px;
|
||||
border-radius: 2px;
|
||||
background: var(--bg-robin-400);
|
||||
}
|
||||
}
|
||||
.explorer-columns::-webkit-scrollbar {
|
||||
height: 0px; /* Height of the scrollbar */
|
||||
}
|
||||
|
||||
.action-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
padding: 0px 16px;
|
||||
border-radius: 2px;
|
||||
background: var(--bg-robin-400);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.explorer-columns-search {
|
||||
border: 1px solid rgba(118, 136, 201, 0.12);
|
||||
border-radius: 6px;
|
||||
padding: 0px;
|
||||
background:#141414;
|
||||
> input {
|
||||
height: 32px;
|
||||
padding: 0 6px;
|
||||
}
|
||||
|
||||
|
||||
border: 1px solid rgba(118, 136, 201, 0.12);
|
||||
border-radius: 6px;
|
||||
padding: 0px;
|
||||
background: #141414;
|
||||
> input {
|
||||
height: 32px;
|
||||
padding: 0 6px;
|
||||
}
|
||||
}
|
||||
|
||||
.explorer-columns-dropdown {
|
||||
height: 200px;
|
||||
background-color: var(--bg-slate-500);
|
||||
overflow: hidden !important;
|
||||
.ant-dropdown-menu {
|
||||
padding: 0;
|
||||
height: 200px;
|
||||
background-color: var(--bg-slate-500);
|
||||
overflow: hidden !important;
|
||||
.ant-dropdown-menu {
|
||||
padding: 0;
|
||||
|
||||
.ant-dropdown-menu-item {
|
||||
padding: 4px;
|
||||
.ant-checkbox-wrapper {
|
||||
padding: 2px 8px !important;
|
||||
}
|
||||
.ant-dropdown-menu-item {
|
||||
padding: 4px;
|
||||
.ant-checkbox-wrapper {
|
||||
padding: 2px 8px !important;
|
||||
}
|
||||
|
||||
.attribute-columns {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 160px;
|
||||
overflow: scroll;
|
||||
}
|
||||
.attribute-columns {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 160px;
|
||||
overflow: scroll;
|
||||
}
|
||||
|
||||
.attribute-columns::-webkit-scrollbar {
|
||||
width: 3px; /* Width of the scrollbar */
|
||||
}
|
||||
|
||||
.attribute-columns::-webkit-scrollbar-track {
|
||||
background: var(--bg-slate-500); /* Color of the track */
|
||||
}
|
||||
|
||||
.attribute-columns::-webkit-scrollbar-thumb {
|
||||
background: var(--bg-vanilla-400); /* Color of the thumb */
|
||||
border-radius: 4px; /* Roundness of the thumb */
|
||||
}
|
||||
|
||||
.attribute-columns::-webkit-scrollbar-thumb:hover {
|
||||
background: var(--bg-vanilla-300); /* Color of the thumb on hover */
|
||||
}
|
||||
}
|
||||
}
|
||||
.attribute-columns::-webkit-scrollbar {
|
||||
width: 3px; /* Width of the scrollbar */
|
||||
}
|
||||
|
||||
.attribute-columns::-webkit-scrollbar-track {
|
||||
background: var(--bg-slate-500); /* Color of the track */
|
||||
}
|
||||
|
||||
.attribute-columns::-webkit-scrollbar-thumb {
|
||||
background: var(--bg-vanilla-400); /* Color of the thumb */
|
||||
border-radius: 4px; /* Roundness of the thumb */
|
||||
}
|
||||
|
||||
.attribute-columns::-webkit-scrollbar-thumb:hover {
|
||||
background: var(--bg-vanilla-300); /* Color of the thumb on hover */
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.lightMode {
|
||||
.explorer-columns-renderer {
|
||||
.explorer-columns-renderer {
|
||||
.ant-divider {
|
||||
border: 0.5px solid var(--bg-vanilla-300);
|
||||
}
|
||||
|
||||
.ant-divider {
|
||||
border: 0.5px solid var(--bg-vanilla-300);
|
||||
}
|
||||
.explorer-columns {
|
||||
.explorer-column-card {
|
||||
border: 1px solid var(--colorBorder, rgba(118, 136, 201, 0.12));
|
||||
background: var(--bg-vanilla-200);
|
||||
}
|
||||
}
|
||||
|
||||
.explorer-columns {
|
||||
.explorer-column-card {
|
||||
border: 1px solid var(--colorBorder, rgba(118, 136, 201, 0.12));
|
||||
background: var(--bg-vanilla-200);
|
||||
}
|
||||
}
|
||||
.explorer-columns-search {
|
||||
border: 1px solid rgba(118, 136, 201, 0.12);
|
||||
}
|
||||
}
|
||||
|
||||
.explorer-columns-search {
|
||||
border: 1px solid rgba(118, 136, 201, 0.12);
|
||||
}
|
||||
}
|
||||
.explorer-columns-dropdown {
|
||||
background-color: var(--bg-vanilla-100);
|
||||
|
||||
.explorer-columns-dropdown {
|
||||
background-color: var(--bg-vanilla-100);
|
||||
.ant-dropdown-menu-item {
|
||||
.attribute-columns {
|
||||
&::-webkit-scrollbar {
|
||||
width: 3px; /* Width of the scrollbar */
|
||||
}
|
||||
|
||||
.ant-dropdown-menu-item {
|
||||
.attribute-columns {
|
||||
&::-webkit-scrollbar {
|
||||
width: 3px; /* Width of the scrollbar */
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-track {
|
||||
background: var(--bg-vanilla-200); /* Color of the track */
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-thumb {
|
||||
background: var(--bg-vanilla-400); /* Color of the thumb */
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-thumb:hover {
|
||||
background: var(--bg-vanilla-300); /* Color of the thumb on hover */
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
&::-webkit-scrollbar-track {
|
||||
background: var(--bg-vanilla-200); /* Color of the track */
|
||||
}
|
||||
|
||||
.explorer-columns-search {
|
||||
background: var(--bg-vanilla-100);
|
||||
}
|
||||
&::-webkit-scrollbar-thumb {
|
||||
background: var(--bg-vanilla-400); /* Color of the thumb */
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-thumb:hover {
|
||||
background: var(--bg-vanilla-300); /* Color of the thumb on hover */
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.explorer-columns-search {
|
||||
background: var(--bg-vanilla-100);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,16 @@
|
||||
.query-section-left-container {
|
||||
border: none;
|
||||
border-top: 1px solid var(--Slate-400, #1d212d);
|
||||
background: var(--Ink-500, #0b0c0e);
|
||||
|
||||
.ant-card-body {
|
||||
padding: 0px;
|
||||
}
|
||||
}
|
||||
|
||||
.lightMode {
|
||||
.query-section-left-container {
|
||||
border-top: 1px solid var(--bg-vanilla-300);
|
||||
background: var(--bg-vanilla-100);
|
||||
}
|
||||
}
|
@ -1,7 +1,8 @@
|
||||
.query-header-container {
|
||||
.action-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
padding: 0px 8px 0px 16px;
|
||||
.action-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
|
@ -21,7 +21,11 @@ function ClickHouseQueryContainer(): JSX.Element | null {
|
||||
queryData={q}
|
||||
/>
|
||||
))}
|
||||
<QueryButton onClick={addQueryHandler} icon={<PlusOutlined />}>
|
||||
<QueryButton
|
||||
onClick={addQueryHandler}
|
||||
icon={<PlusOutlined />}
|
||||
style={{ margin: '0.4rem 1rem' }}
|
||||
>
|
||||
Query
|
||||
</QueryButton>
|
||||
</>
|
||||
|
@ -25,7 +25,11 @@ function PromQLQueryContainer(): JSX.Element | null {
|
||||
/>
|
||||
),
|
||||
)}
|
||||
<QueryButton onClick={addQueryHandler} icon={<PlusOutlined />}>
|
||||
<QueryButton
|
||||
onClick={addQueryHandler}
|
||||
icon={<PlusOutlined />}
|
||||
style={{ margin: '0.4rem 1rem' }}
|
||||
>
|
||||
Query
|
||||
</QueryButton>
|
||||
</>
|
||||
|
@ -8,6 +8,15 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 4px;
|
||||
color: #fff;
|
||||
font-family: Inter;
|
||||
font-size: 12px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 18px; /* 150% */
|
||||
letter-spacing: -0.06px;
|
||||
padding: 7px 23px;
|
||||
|
||||
.prom-ql-icon {
|
||||
height: 14px;
|
||||
@ -17,6 +26,7 @@
|
||||
}
|
||||
.ant-btn-default {
|
||||
border-color: transparent;
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
.ant-tabs-tab-active {
|
||||
@ -27,16 +37,26 @@
|
||||
|
||||
.ant-tabs-nav {
|
||||
margin: 0px;
|
||||
margin-bottom: 0.5rem;
|
||||
|
||||
.ant-tabs-nav-wrap {
|
||||
padding: 8px 16px;
|
||||
}
|
||||
|
||||
.ant-tabs-extra-content {
|
||||
padding-right: 8px;
|
||||
}
|
||||
}
|
||||
.ant-tabs-nav::before {
|
||||
border-bottom: none !important;
|
||||
}
|
||||
.ant-tabs-nav-list {
|
||||
border: 1px solid var(--bg-slate-200);
|
||||
border-radius: 2px;
|
||||
border: 1px solid var(--bg-slate-400);
|
||||
background: var(--bg-ink-400);
|
||||
box-shadow: 0px 0px 8px 0px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
.ant-tabs-tab + .ant-tabs-tab {
|
||||
border-left: 1px solid var(--bg-slate-200) !important;
|
||||
border-left: 1px solid var(--bg-slate-400) !important;
|
||||
}
|
||||
.stage-run-query {
|
||||
display: flex;
|
||||
@ -46,11 +66,16 @@
|
||||
|
||||
.lightMode {
|
||||
.dashboard-navigation {
|
||||
.nav-btns {
|
||||
color: var(---bg-ink-300);
|
||||
background-color: var(--bg-vanilla-200);
|
||||
}
|
||||
.ant-tabs-nav-list {
|
||||
border: 1px solid var(--bg-vanilla-300);
|
||||
background: var(--bg-vanilla-300);
|
||||
}
|
||||
.ant-tabs-tab + .ant-tabs-tab {
|
||||
border-left: 1px solid var(--bg-vanilla-200) !important;
|
||||
border-left: 1px solid var(--bg-vanilla-100) !important;
|
||||
}
|
||||
.ant-tabs-tab-active {
|
||||
.nav-btns {
|
||||
|
@ -147,11 +147,10 @@ function QuerySection({
|
||||
{
|
||||
key: EQueryType.QUERY_BUILDER,
|
||||
label: (
|
||||
<Tooltip title="Query Builder">
|
||||
<Button className="nav-btns">
|
||||
<Atom size={14} />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
<Button className="nav-btns">
|
||||
<Atom size={14} />
|
||||
<Typography>Query Builder</Typography>
|
||||
</Button>
|
||||
),
|
||||
tab: <Typography>Query Builder</Typography>,
|
||||
children: (
|
||||
@ -169,11 +168,10 @@ function QuerySection({
|
||||
{
|
||||
key: EQueryType.QUERY_BUILDER,
|
||||
label: (
|
||||
<Tooltip title="Query Builder">
|
||||
<Button className="nav-btns">
|
||||
<Atom size={14} />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
<Button className="nav-btns">
|
||||
<Atom size={14} />
|
||||
<Typography>Query Builder</Typography>
|
||||
</Button>
|
||||
),
|
||||
tab: <Typography>Query Builder</Typography>,
|
||||
children: (
|
||||
@ -187,11 +185,10 @@ function QuerySection({
|
||||
{
|
||||
key: EQueryType.CLICKHOUSE,
|
||||
label: (
|
||||
<Tooltip title="ClickHouse">
|
||||
<Button className="nav-btns">
|
||||
<Terminal size={14} />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
<Button className="nav-btns">
|
||||
<Terminal size={14} />
|
||||
<Typography>ClickHouse Query</Typography>
|
||||
</Button>
|
||||
),
|
||||
tab: <Typography>ClickHouse Query</Typography>,
|
||||
children: <ClickHouseQueryContainer />,
|
||||
@ -204,6 +201,7 @@ function QuerySection({
|
||||
<PromQLIcon
|
||||
fillColor={isDarkMode ? Color.BG_VANILLA_200 : Color.BG_INK_300}
|
||||
/>
|
||||
<Typography>PromQL</Typography>
|
||||
</Button>
|
||||
</Tooltip>
|
||||
),
|
||||
|
@ -1,28 +1,14 @@
|
||||
import { EQueryType } from 'types/common/dashboard';
|
||||
|
||||
import { Tag } from '../styles';
|
||||
|
||||
function QueryTypeTag({ queryType }: IQueryTypeTagProps): JSX.Element {
|
||||
switch (queryType) {
|
||||
case EQueryType.QUERY_BUILDER:
|
||||
return (
|
||||
<span>
|
||||
<Tag color="geekblue">Query Builder</Tag>
|
||||
</span>
|
||||
);
|
||||
return <span>Query Builder</span>;
|
||||
|
||||
case EQueryType.CLICKHOUSE:
|
||||
return (
|
||||
<span>
|
||||
<Tag color="orange">ClickHouse Query</Tag>
|
||||
</span>
|
||||
);
|
||||
return <span>ClickHouse Query</span>;
|
||||
case EQueryType.PROM:
|
||||
return (
|
||||
<span>
|
||||
<Tag color="green">PromQL</Tag>
|
||||
</span>
|
||||
);
|
||||
return <span>PromQL</span>;
|
||||
default:
|
||||
return <span />;
|
||||
}
|
||||
|
@ -1,8 +1,8 @@
|
||||
import { PANEL_TYPES } from 'constants/queryBuilder';
|
||||
import { Spline } from 'lucide-react';
|
||||
import { EQueryType } from 'types/common/dashboard';
|
||||
|
||||
import QueryTypeTag from '../QueryTypeTag';
|
||||
import { PlotTagWrapperStyled } from './styles';
|
||||
|
||||
interface IPlotTagProps {
|
||||
queryType: EQueryType;
|
||||
@ -15,9 +15,10 @@ function PlotTag({ queryType, panelType }: IPlotTagProps): JSX.Element | null {
|
||||
}
|
||||
|
||||
return (
|
||||
<PlotTagWrapperStyled $panelType={panelType}>
|
||||
Plotted using <QueryTypeTag queryType={queryType} />
|
||||
</PlotTagWrapperStyled>
|
||||
<div className="plot-tag">
|
||||
<Spline size={14} />
|
||||
Plotted with <QueryTypeTag queryType={queryType} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,42 @@
|
||||
.widget-graph {
|
||||
border: none;
|
||||
background-color: unset;
|
||||
background-image: radial-gradient(var(--bg-slate-400) 1px, transparent 0);
|
||||
background-size: 20px 20px;
|
||||
padding: 16px;
|
||||
|
||||
.header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
|
||||
.plot-tag {
|
||||
display: inline-flex;
|
||||
padding: 4px 4px 4px 6px;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
border-radius: 4px;
|
||||
background: var(--Slate-400, #1d212d);
|
||||
backdrop-filter: blur(6px);
|
||||
width: fit-content;
|
||||
}
|
||||
}
|
||||
|
||||
.header:has(.date-time-selector:only-child) {
|
||||
justify-content: end;
|
||||
}
|
||||
}
|
||||
|
||||
.lightMode {
|
||||
.widget-graph {
|
||||
background-color: var(--bg-vanilla-100);
|
||||
background-image: radial-gradient(var(--bg-vanilla-400) 1px, transparent 0);
|
||||
background-size: 20px 20px;
|
||||
|
||||
.header {
|
||||
.plot-tag {
|
||||
background: var(--bg-vanilla-300);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -2,6 +2,7 @@ import { QueryParams } from 'constants/query';
|
||||
import { PANEL_TYPES } from 'constants/queryBuilder';
|
||||
import PanelWrapper from 'container/PanelWrapper/PanelWrapper';
|
||||
import { CustomTimeType } from 'container/TopNav/DateTimeSelectionV2/config';
|
||||
import { useIsDarkMode } from 'hooks/useDarkMode';
|
||||
import useUrlQuery from 'hooks/useUrlQuery';
|
||||
import { GetQueryResultsProps } from 'lib/dashboard/getQueryResults';
|
||||
import GetMinMax from 'lib/getMinMax';
|
||||
@ -84,8 +85,24 @@ function WidgetGraph({
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
const isDarkMode = useIsDarkMode();
|
||||
|
||||
return (
|
||||
<div ref={graphRef} style={{ height: '100%' }}>
|
||||
<div
|
||||
ref={graphRef}
|
||||
style={{
|
||||
height: '80%',
|
||||
width: '80%',
|
||||
margin: 'auto auto',
|
||||
borderRadius: '3px',
|
||||
border: isDarkMode
|
||||
? '1px solid var(--bg-slate-500)'
|
||||
: '1px solid var(--bg-vanilla-300)',
|
||||
background: isDarkMode
|
||||
? 'linear-gradient(0deg, rgba(171, 189, 255, 0.00) 0%, rgba(171, 189, 255, 0.00) 100%), #0B0C0E'
|
||||
: 'var(--bg-vanilla-100)',
|
||||
}}
|
||||
>
|
||||
<PanelWrapper
|
||||
widget={selectedWidget}
|
||||
queryResponse={queryResponse}
|
||||
|
@ -1,6 +1,10 @@
|
||||
import './WidgetGraph.styles.scss';
|
||||
|
||||
import { InfoCircleOutlined } from '@ant-design/icons';
|
||||
import { Card } from 'container/GridCardLayout/styles';
|
||||
import DateTimeSelectionV2 from 'container/TopNav/DateTimeSelectionV2';
|
||||
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
|
||||
import { useIsDarkMode } from 'hooks/useDarkMode';
|
||||
import { memo } from 'react';
|
||||
|
||||
import { WidgetGraphContainerProps } from '../../types';
|
||||
@ -16,13 +20,22 @@ function WidgetGraph({
|
||||
}: WidgetGraphContainerProps): JSX.Element {
|
||||
const { currentQuery } = useQueryBuilder();
|
||||
|
||||
const isDarkMode = useIsDarkMode();
|
||||
|
||||
if (selectedWidget === undefined) {
|
||||
return <Card $panelType={selectedGraph}>Invalid widget</Card>;
|
||||
return (
|
||||
<Card $panelType={selectedGraph} isDarkMode={isDarkMode}>
|
||||
Invalid widget
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Container $panelType={selectedGraph}>
|
||||
<PlotTag queryType={currentQuery.queryType} panelType={selectedGraph} />
|
||||
<Container $panelType={selectedGraph} className="widget-graph">
|
||||
<div className="header">
|
||||
<PlotTag queryType={currentQuery.queryType} panelType={selectedGraph} />
|
||||
<DateTimeSelectionV2 showAutoRefresh={false} hideShareModal />
|
||||
</div>
|
||||
{queryResponse.error && (
|
||||
<AlertIconContainer color="red" title={queryResponse.error.message}>
|
||||
<InfoCircleOutlined />
|
||||
|
@ -12,13 +12,10 @@ export const Container = styled(Card)<Props>`
|
||||
}
|
||||
|
||||
.ant-card-body {
|
||||
padding: ${({ $panelType }): string =>
|
||||
$panelType === PANEL_TYPES.TABLE || $panelType === PANEL_TYPES.LIST
|
||||
? '0 0'
|
||||
: '1.5rem 0'};
|
||||
height: 60vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 0px;
|
||||
}
|
||||
`;
|
||||
|
||||
|
@ -1,3 +1,5 @@
|
||||
import './LeftContainer.styles.scss';
|
||||
|
||||
import { DEFAULT_ENTITY_VERSION } from 'constants/app';
|
||||
import { initialQueriesMap, PANEL_TYPES } from 'constants/queryBuilder';
|
||||
import { REACT_QUERY_KEY } from 'constants/reactQueryKeys';
|
||||
@ -97,7 +99,7 @@ function LeftContainer({
|
||||
setRequestData={setRequestData}
|
||||
selectedWidget={selectedWidget}
|
||||
/>
|
||||
<QueryContainer>
|
||||
<QueryContainer className="query-section-left-container">
|
||||
<QuerySection selectedGraph={selectedGraph} queryResponse={queryResponse} />
|
||||
{selectedGraph === PANEL_TYPES.LIST && (
|
||||
<ExplorerColumnsRenderer
|
||||
|
@ -3,11 +3,6 @@ import styled from 'styled-components';
|
||||
|
||||
export const QueryContainer = styled(Card)`
|
||||
&&& {
|
||||
margin-top: 1rem;
|
||||
min-height: 23.5%;
|
||||
}
|
||||
|
||||
.ant-card-body {
|
||||
padding: 12px;
|
||||
}
|
||||
`;
|
||||
|
@ -2,3 +2,78 @@
|
||||
display: grid;
|
||||
grid-template-columns: 1fr max-content;
|
||||
}
|
||||
|
||||
.edit-header {
|
||||
display: flex;
|
||||
height: 48px;
|
||||
flex-shrink: 0;
|
||||
border-bottom: 1px solid var(--bg-slate-500);
|
||||
background: var(--bg-ink-500);
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 0px 12px 0px 16px;
|
||||
|
||||
.left-header {
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
align-items: center;
|
||||
|
||||
.discard-icon {
|
||||
color: var(--bg-vanilla-100);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.configure-panel {
|
||||
color: var(--bg-vanilla-100);
|
||||
font-family: Inter;
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 20px; /* 142.857% */
|
||||
letter-spacing: -0.07px;
|
||||
border-left: 1px solid var(--bg-slate-500);
|
||||
padding-left: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
.save-btn {
|
||||
display: flex;
|
||||
height: 32px;
|
||||
width: 121px;
|
||||
padding: 4px 12px 4px 10px;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
border-radius: 2px;
|
||||
background: var(--bg-robin-500);
|
||||
color: var(--bg-vanilla-100);
|
||||
text-align: center;
|
||||
font-family: Inter;
|
||||
font-size: 12px;
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
line-height: 18px; /* 150% */
|
||||
|
||||
.ant-btn-icon {
|
||||
margin-inline-end: 0px !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.lightMode {
|
||||
.edit-header {
|
||||
border-bottom: 1px solid var(--bg-vanilla-300);
|
||||
background: var(--bg-vanilla-200);
|
||||
|
||||
.left-header {
|
||||
.discard-icon {
|
||||
color: var(--bg-ink-300);
|
||||
}
|
||||
|
||||
.configure-panel {
|
||||
color: var(--bg-slate-300);
|
||||
border-left: 1px solid var(--bg-vanilla-300);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,414 @@
|
||||
.right-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.header {
|
||||
display: flex;
|
||||
padding: 14px 14px 14px 12px;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
|
||||
.purple-dot {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border-radius: 2px;
|
||||
background: var(--bg-robin-400);
|
||||
}
|
||||
.header-text {
|
||||
color: var(--bg-vanilla-400);
|
||||
font-family: Inter;
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 20px; /* 142.857% */
|
||||
letter-spacing: -0.07px;
|
||||
}
|
||||
}
|
||||
|
||||
.name-description {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 12px 12px 16px 12px;
|
||||
border-top: 1px solid var(--bg-slate-500);
|
||||
border-bottom: 1px solid var(--bg-slate-500);
|
||||
gap: 8px;
|
||||
|
||||
.typography {
|
||||
color: var(--bg-vanilla-400);
|
||||
font-family: 'Space Mono';
|
||||
font-size: 13px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 18px; /* 138.462% */
|
||||
letter-spacing: 0.52px;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.name-input {
|
||||
display: flex;
|
||||
padding: 6px 6px 6px 8px;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
flex: 1 0 0;
|
||||
align-self: stretch;
|
||||
border-radius: 2px;
|
||||
border: 1px solid var(--bg-slate-400);
|
||||
background: var(--bg-ink-300);
|
||||
color: var(--bg-vanilla-100);
|
||||
font-family: Inter;
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 18px; /* 128.571% */
|
||||
letter-spacing: -0.07px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.description-input {
|
||||
border-style: unset;
|
||||
.ant-input {
|
||||
display: flex;
|
||||
height: 80px;
|
||||
padding: 6px 6px 6px 8px;
|
||||
align-items: flex-start;
|
||||
gap: 4px;
|
||||
border-radius: 2px;
|
||||
border: 1px solid var(--bg-slate-400);
|
||||
background: var(--bg-ink-300);
|
||||
color: var(--bg-vanilla-100);
|
||||
font-family: Inter;
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 18px; /* 128.571% */
|
||||
letter-spacing: -0.07px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.panel-config {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 12px 12px 16px 12px;
|
||||
gap: 8px;
|
||||
border-bottom: 1px solid var(--bg-slate-500);
|
||||
|
||||
.typography {
|
||||
color: var(--bg-vanilla-400);
|
||||
font-family: 'Space Mono';
|
||||
font-size: 13px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 18px; /* 138.462% */
|
||||
letter-spacing: 0.52px;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.panel-type-select {
|
||||
.ant-select-selector {
|
||||
display: flex;
|
||||
height: 32px;
|
||||
padding: 6px 6px 6px 8px;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
flex-shrink: 0;
|
||||
border-radius: 2px;
|
||||
border: 1px solid var(--bg-slate-400);
|
||||
background: var(--bg-ink-300);
|
||||
}
|
||||
|
||||
.select-option {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
.icon {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.display {
|
||||
color: var(--bg-vanilla-100);
|
||||
font-family: Inter;
|
||||
font-size: 12px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 16px; /* 133.333% */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.fill-gaps {
|
||||
margin-top: 16px;
|
||||
display: flex;
|
||||
padding: 12px;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
border-radius: 2px;
|
||||
border: 1px solid var(--bg-slate-400);
|
||||
background: var(--bg-ink-400);
|
||||
|
||||
.fill-gaps-text {
|
||||
color: var(--bg-vanilla-400);
|
||||
font-family: 'Space Mono';
|
||||
font-size: 13px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 18px; /* 138.462% */
|
||||
letter-spacing: 0.52px;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
}
|
||||
|
||||
.panel-time-text {
|
||||
margin-top: 16px;
|
||||
color: var(--bg-vanilla-400);
|
||||
font-family: 'Space Mono';
|
||||
font-size: 13px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 18px; /* 138.462% */
|
||||
letter-spacing: 0.52px;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.y-axis-unit-selector {
|
||||
margin-top: 16px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
|
||||
.heading {
|
||||
color: var(--bg-vanilla-400);
|
||||
font-family: 'Space Mono';
|
||||
font-size: 13px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 18px; /* 138.462% */
|
||||
letter-spacing: 0.52px;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.input {
|
||||
display: flex;
|
||||
height: 32px;
|
||||
padding: 6px 6px 6px 8px;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
align-self: stretch;
|
||||
border-radius: 2px;
|
||||
border: 1px solid var(--bg-slate-400);
|
||||
background: var(--bg-ink-300);
|
||||
|
||||
.ant-input {
|
||||
background: var(--bg-ink-300);
|
||||
}
|
||||
}
|
||||
}
|
||||
.soft-min-max {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-top: 4px;
|
||||
gap: 12px;
|
||||
|
||||
.container {
|
||||
display: flex;
|
||||
height: 32px;
|
||||
align-items: center;
|
||||
width: 50%;
|
||||
border-radius: 2px;
|
||||
border: 1px solid var(--bg-slate-400);
|
||||
background: var(--bg-ink-400);
|
||||
box-shadow: 0px 0px 8px 0px rgba(0, 0, 0, 0.1);
|
||||
|
||||
.text {
|
||||
color: var(--bg-vanilla-400);
|
||||
font-family: 'Space Mono';
|
||||
font-size: 12px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 16px; /* 133.333% */
|
||||
letter-spacing: 0.48px;
|
||||
text-transform: uppercase;
|
||||
width: 50%;
|
||||
padding: 8px;
|
||||
}
|
||||
.input {
|
||||
width: 50%;
|
||||
border: none;
|
||||
border-left: 1px solid var(--bg-slate-400);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.alerts {
|
||||
display: flex;
|
||||
padding: 12px;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
border-bottom: 1px solid var(--bg-slate-500);
|
||||
cursor: pointer;
|
||||
|
||||
.left-section {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
|
||||
.bell-icon {
|
||||
color: var(--bg-vanilla-400);
|
||||
}
|
||||
|
||||
.alerts-text {
|
||||
color: var(--bg-vanilla-400);
|
||||
font-family: Inter;
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 20px; /* 142.857% */
|
||||
letter-spacing: 0.14px;
|
||||
}
|
||||
}
|
||||
.plus-icon {
|
||||
color: var(--bg-vanilla-400);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.select-option {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
.icon {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.display {
|
||||
color: var(--bg-vanilla-100);
|
||||
font-family: Inter;
|
||||
font-size: 12px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 16px; /* 133.333% */
|
||||
}
|
||||
}
|
||||
|
||||
.lightMode {
|
||||
.right-container {
|
||||
background-color: var(--bg-vanilla-100);
|
||||
.header {
|
||||
.header-text {
|
||||
color: var(--bg-ink-400);
|
||||
}
|
||||
}
|
||||
|
||||
.name-description {
|
||||
border-top: 1px solid var(--bg-vanilla-300);
|
||||
border-bottom: 1px solid var(--bg-vanilla-300);
|
||||
|
||||
.typography {
|
||||
color: var(--bg-ink-400);
|
||||
}
|
||||
|
||||
.name-input {
|
||||
border: 1px solid var(--bg-vanilla-300);
|
||||
background: var(--bg-vanilla-300);
|
||||
color: var(--bg-ink-300);
|
||||
}
|
||||
|
||||
.description-input {
|
||||
.ant-input {
|
||||
border: 1px solid var(--bg-vanilla-300);
|
||||
background: var(--bg-vanilla-300);
|
||||
color: var(--bg-ink-300);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.panel-config {
|
||||
border-bottom: 1px solid var(--bg-vanilla-300);
|
||||
|
||||
.typography {
|
||||
color: var(--bg-ink-400);
|
||||
}
|
||||
|
||||
.panel-type-select {
|
||||
.ant-select-selector {
|
||||
border: 1px solid var(--bg-vanilla-300);
|
||||
background: var(--bg-vanilla-300);
|
||||
}
|
||||
|
||||
.select-option {
|
||||
.display {
|
||||
color: var(--bg-ink-300);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.fill-gaps {
|
||||
border: 1px solid var(--bg-vanilla-300);
|
||||
background: var(--bg-vanilla-300);
|
||||
|
||||
.fill-gaps-text {
|
||||
color: var(--bg-ink-400);
|
||||
}
|
||||
}
|
||||
|
||||
.panel-time-text {
|
||||
color: var(--bg-ink-400);
|
||||
}
|
||||
|
||||
.y-axis-unit-selector {
|
||||
.heading {
|
||||
color: var(--bg-ink-400);
|
||||
}
|
||||
|
||||
.input {
|
||||
border: 1px solid var(--bg-vanilla-300);
|
||||
background: var(--bg-vanilla-300);
|
||||
|
||||
.ant-input {
|
||||
background: var(--bg-vanilla-300);
|
||||
}
|
||||
}
|
||||
}
|
||||
.soft-min-max {
|
||||
.container {
|
||||
border: 1px solid var(--bg-vanilla-300);
|
||||
background: var(--bg-vanilla-300);
|
||||
|
||||
.text {
|
||||
color: var(--bg-ink-300);
|
||||
}
|
||||
.input {
|
||||
border-left: 1px solid var(--bg-vanilla-300);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.alerts {
|
||||
border-bottom: 1px solid var(--bg-vanilla-300);
|
||||
|
||||
.left-section {
|
||||
.bell-icon {
|
||||
color: var(--bg-ink-300);
|
||||
}
|
||||
|
||||
.alerts-text {
|
||||
color: var(--bg-ink-300);
|
||||
}
|
||||
}
|
||||
.plus-icon {
|
||||
color: var(--bg-ink-300);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.select-option {
|
||||
.display {
|
||||
color: var(--bg-ink-100);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,7 +1,27 @@
|
||||
.color-selector-button {
|
||||
border: none;
|
||||
.color-selector-space {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.color-selector-light {
|
||||
border: 1px solid #d9d9d9;
|
||||
.color-selector-button {
|
||||
border: none;
|
||||
width: 100%;
|
||||
|
||||
.ant-btn {
|
||||
box-shadow: none;
|
||||
background-color: unset;
|
||||
}
|
||||
}
|
||||
|
||||
.lightMode {
|
||||
.color-selector-button {
|
||||
background-color: var(--bg-vanilla-300);
|
||||
|
||||
.ant-btn {
|
||||
box-shadow: none;
|
||||
background-color: unset;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4,7 +4,6 @@ import { DownOutlined } from '@ant-design/icons';
|
||||
import { Button, ColorPicker, Dropdown, Space } from 'antd';
|
||||
import { Color } from 'antd/es/color-picker';
|
||||
import { MenuProps } from 'antd/lib';
|
||||
import { useIsDarkMode } from 'hooks/useDarkMode';
|
||||
import useDebounce from 'hooks/useDebounce';
|
||||
import { Dispatch, SetStateAction, useEffect, useState } from 'react';
|
||||
|
||||
@ -18,8 +17,6 @@ function ColorSelector({
|
||||
|
||||
const debounceColor = useDebounce(colorFromPicker);
|
||||
|
||||
const isDarkMode = useIsDarkMode();
|
||||
|
||||
useEffect(() => {
|
||||
if (debounceColor) {
|
||||
setColor(debounceColor);
|
||||
@ -69,11 +66,9 @@ function ColorSelector({
|
||||
<Dropdown menu={{ items }} trigger={['click']}>
|
||||
<Button
|
||||
onClick={(e): void => e.preventDefault()}
|
||||
className={
|
||||
isDarkMode ? 'color-selector-button' : 'color-selector-button-light'
|
||||
}
|
||||
className="color-selector-button"
|
||||
>
|
||||
<Space>
|
||||
<Space className="color-selector-space">
|
||||
<CustomColor color={thresholdColor} />
|
||||
<DownOutlined />
|
||||
</Space>
|
||||
|
@ -1,18 +1,29 @@
|
||||
.custom-color-container {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
align-items: center;
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
align-items: center;
|
||||
|
||||
.custom-color-typography-dark {
|
||||
color: #fff !important;
|
||||
}
|
||||
.custom-color-typography-dark {
|
||||
color: #fff !important;
|
||||
font-family: 'Space Mono';
|
||||
font-size: 12px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 16px; /* 133.333% */
|
||||
}
|
||||
|
||||
.custom-color-typography-light {
|
||||
color: #000 !important;
|
||||
}
|
||||
.custom-color-typography-light {
|
||||
color: #000 !important;
|
||||
font-family: 'Space Mono';
|
||||
font-size: 12px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 16px; /* 133.333% */
|
||||
}
|
||||
|
||||
.custom-color-tag {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
}
|
||||
}
|
||||
.custom-color-tag {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
border-radius: 12px;
|
||||
}
|
||||
}
|
||||
|
@ -1,14 +1,13 @@
|
||||
.show-case-container {
|
||||
padding: 5px 15px;
|
||||
border-radius: 5px;
|
||||
display: inline-block;
|
||||
padding: 5px 15px;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.show-case-dark {
|
||||
background-color: #141414;
|
||||
background-color: #141414;
|
||||
}
|
||||
|
||||
.show-case-light {
|
||||
background-color: rgb(255, 255, 255);
|
||||
border: 1px solid #141414;
|
||||
}
|
||||
background-color: rgb(255, 255, 255);
|
||||
border: 1px solid #141414;
|
||||
}
|
||||
|
@ -1,18 +1,24 @@
|
||||
import './ShowCaseValue.styles.scss';
|
||||
|
||||
import cx from 'classnames';
|
||||
import { useIsDarkMode } from 'hooks/useDarkMode';
|
||||
|
||||
import { ShowCaseValueProps } from './types';
|
||||
|
||||
function ShowCaseValue({ width, value }: ShowCaseValueProps): JSX.Element {
|
||||
function ShowCaseValue({
|
||||
width,
|
||||
value,
|
||||
className = '',
|
||||
}: ShowCaseValueProps): JSX.Element {
|
||||
const isDarkMode = useIsDarkMode();
|
||||
return (
|
||||
<div
|
||||
className={
|
||||
className={cx(
|
||||
isDarkMode
|
||||
? `show-case-container show-case-dark`
|
||||
: `show-case-container show-case-light`
|
||||
}
|
||||
: `show-case-container show-case-light`,
|
||||
className,
|
||||
)}
|
||||
style={{ minWidth: width }}
|
||||
>
|
||||
{value}
|
||||
|
@ -1,58 +1,427 @@
|
||||
.operator-input-root {
|
||||
width: auto !important;
|
||||
color: var(--bg-vanilla-400);
|
||||
font-family: 'Space Mono';
|
||||
font-size: 12px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 16px; /* 133.333% */
|
||||
letter-spacing: 0.48px;
|
||||
}
|
||||
.threshold-container {
|
||||
margin-top: 12px;
|
||||
|
||||
.threshold-card {
|
||||
padding: 0px;
|
||||
border-radius: 10px;
|
||||
position: relative;
|
||||
margin-top: 10px;
|
||||
.threshold-card-container {
|
||||
display: flex;
|
||||
position: relative;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 12px;
|
||||
flex-shrink: 0;
|
||||
padding: 12px;
|
||||
border-radius: 2px;
|
||||
border: 1px solid var(--bg-slate-500);
|
||||
background: var(--bg-ink-400);
|
||||
|
||||
.threshold-card-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
}
|
||||
.edit-action-btns {
|
||||
position: absolute;
|
||||
right: -8px;
|
||||
top: -14px;
|
||||
display: none;
|
||||
|
||||
.ant-typography {
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
line-height: 22px;
|
||||
}
|
||||
.ant-btn {
|
||||
display: flex;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
padding: 4px;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
flex-shrink: 0;
|
||||
background: var(--bg-slate-400);
|
||||
}
|
||||
|
||||
.ant-typograph-dark {
|
||||
color: #FFFFFF73;
|
||||
}
|
||||
.ant-btn + .ant-btn {
|
||||
border-left: 1px solid var(--bg-slate-200);
|
||||
}
|
||||
|
||||
.ant-typograph-light {
|
||||
color: #00000073;
|
||||
}
|
||||
.edit-btn {
|
||||
border-radius: 2px 0px 0px 2px;
|
||||
}
|
||||
|
||||
}
|
||||
.delete-btn {
|
||||
border-radius: 0px 2px 2px 0px;
|
||||
}
|
||||
}
|
||||
|
||||
.threshold-card-dark {
|
||||
background-color: #1F1F1F;
|
||||
}
|
||||
.time-series-alerts {
|
||||
display: flex;
|
||||
border-radius: 2px;
|
||||
border: 1px solid var(--bg-slate-400);
|
||||
background: var(--bg-ink-400);
|
||||
box-shadow: 0px 0px 8px 0px rgba(0, 0, 0, 0.1);
|
||||
width: 100%;
|
||||
align-items: center;
|
||||
|
||||
.threshold-card-light {
|
||||
background-color: rgb(255, 255, 255);
|
||||
}
|
||||
.label {
|
||||
color: var(--bg-vanilla-400);
|
||||
font-family: 'Space Mono';
|
||||
font-size: 12px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 16px; /* 133.333% */
|
||||
letter-spacing: 0.48px;
|
||||
text-transform: uppercase;
|
||||
width: 20%;
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
.threshold-action-button {
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
top: 10px;
|
||||
}
|
||||
.label-input {
|
||||
height: 32px;
|
||||
width: 80%;
|
||||
flex-shrink: 0;
|
||||
border-radius: 2px;
|
||||
border-left: 1px solid var(--bg-slate-400);
|
||||
background: var(--bg-ink-400);
|
||||
box-shadow: 0px 0px 8px 0px rgba(0, 0, 0, 0.1);
|
||||
color: var(--bg-vanilla-100);
|
||||
font-family: 'Space Mono';
|
||||
font-size: 12px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 16px; /* 133.333% */
|
||||
padding: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.threshold-action-icon {
|
||||
font-size: 16px;
|
||||
}
|
||||
.value-table-alerts {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
|
||||
.threshold-units-selector {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.operator-input {
|
||||
.ant-select-selector {
|
||||
background-color: var(--bg-ink-300) !important;
|
||||
min-width: 50px;
|
||||
max-width: 150px;
|
||||
color: var(--bg-vanilla-100);
|
||||
font-family: 'Space Mono';
|
||||
font-size: 12px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 16px; /* 133.333% */
|
||||
}
|
||||
}
|
||||
|
||||
.threshold-color-picker {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
.typography {
|
||||
color: var(--bg-vanilla-400);
|
||||
font-family: 'Space Mono';
|
||||
font-size: 12px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 16px; /* 133.333% */
|
||||
letter-spacing: 0.48px;
|
||||
}
|
||||
.typography-preview {
|
||||
color: var(--bg-vanilla-400);
|
||||
font-family: 'Space Mono';
|
||||
font-size: 12px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 16px; /* 133.333% */
|
||||
letter-spacing: 0.48px;
|
||||
background-color: var(--bg-ink-300) !important;
|
||||
max-width: 150px;
|
||||
overflow-x: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
}
|
||||
.threshold-units-selector {
|
||||
display: flex;
|
||||
border-radius: 2px 2px 0px 0px;
|
||||
border: 1px solid var(--bg-slate-400);
|
||||
background: var(--bg-ink-400);
|
||||
box-shadow: 0px 0px 8px 0px rgba(0, 0, 0, 0.1);
|
||||
height: 32px;
|
||||
flex-shrink: 0;
|
||||
width: 100%;
|
||||
|
||||
.unit-input {
|
||||
width: 50%;
|
||||
border: none;
|
||||
color: var(--bg-vanilla-400);
|
||||
font-family: 'Space Mono';
|
||||
font-size: 12px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 16px; /* 133.333% */
|
||||
background: var(--bg-ink-400);
|
||||
}
|
||||
|
||||
.unit-selection {
|
||||
width: 50%;
|
||||
border: none;
|
||||
border-left: 1px solid var(--bg-slate-400);
|
||||
color: var(--bg-vanilla-400);
|
||||
font-family: 'Space Mono';
|
||||
font-size: 12px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 16px; /* 133.333% */
|
||||
|
||||
.ant-select-selector {
|
||||
border: none;
|
||||
height: unset;
|
||||
color: var(--bg-vanilla-400);
|
||||
font-family: 'Space Mono';
|
||||
font-size: 12px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 16px; /* 133.333% */
|
||||
}
|
||||
}
|
||||
|
||||
.unit-selection-prev {
|
||||
width: 50%;
|
||||
border: none;
|
||||
border-left: 1px solid var(--bg-slate-400);
|
||||
color: var(--bg-vanilla-400);
|
||||
font-family: 'Space Mono';
|
||||
font-size: 12px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 16px; /* 133.333% */
|
||||
background: var(--bg-ink-400);
|
||||
}
|
||||
}
|
||||
|
||||
.thresholds-color-selector {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
border-radius: 0px 0px 2px 2px;
|
||||
border: 1px solid var(--bg-slate-400);
|
||||
border-top: none;
|
||||
background: var(--bg-ink-400);
|
||||
box-shadow: 0px 0px 8px 0px rgba(0, 0, 0, 0.1);
|
||||
height: 32px;
|
||||
flex-shrink: 0;
|
||||
width: 100%;
|
||||
margin-top: -12px;
|
||||
|
||||
.color-selector {
|
||||
width: 50%;
|
||||
color: var(--bg-vanilla-100);
|
||||
font-family: 'Space Mono';
|
||||
font-size: 12px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 16px; /* 133.333% */
|
||||
background: var(--bg-ink-400);
|
||||
|
||||
.ant-btn {
|
||||
box-shadow: none;
|
||||
height: auto;
|
||||
}
|
||||
}
|
||||
.color-format {
|
||||
width: 50%;
|
||||
border: none;
|
||||
border-left: 1px solid var(--bg-slate-400);
|
||||
color: var(--bg-vanilla-100);
|
||||
font-family: 'Space Mono';
|
||||
font-size: 12px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 16px; /* 133.333% */
|
||||
|
||||
.ant-select-selector {
|
||||
border: none;
|
||||
height: unset;
|
||||
color: var(--bg-vanilla-400);
|
||||
font-family: 'Space Mono';
|
||||
font-size: 12px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 16px; /* 133.333% */
|
||||
background-color: unset;
|
||||
}
|
||||
}
|
||||
|
||||
.color-format-prev {
|
||||
width: 50%;
|
||||
border: none;
|
||||
border-left: 1px solid var(--bg-slate-400);
|
||||
color: var(--bg-vanilla-100);
|
||||
font-family: 'Space Mono';
|
||||
font-size: 12px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 16px; /* 133.333% */
|
||||
background: var(--bg-ink-400);
|
||||
}
|
||||
}
|
||||
|
||||
.threshold-action-button {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
gap: 12px;
|
||||
|
||||
.discard-btn {
|
||||
display: flex;
|
||||
width: 50%;
|
||||
align-items: center;
|
||||
height: 34px;
|
||||
padding: 4px 8px 4px 10px;
|
||||
justify-content: center;
|
||||
gap: 6px;
|
||||
flex: 1 0 0;
|
||||
border-radius: 2px;
|
||||
border: 1px solid var(--bg-slate-400);
|
||||
background: var(--bg-slate-500);
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.save-changes {
|
||||
display: flex;
|
||||
width: 50%;
|
||||
align-items: center;
|
||||
height: 34px;
|
||||
padding: 4px 8px 4px 10px;
|
||||
justify-content: center;
|
||||
gap: 6px;
|
||||
flex: 1 0 0;
|
||||
border-radius: 2px;
|
||||
border: 1px solid var(--bg-robin-500);
|
||||
background: var(--bg-robin-500);
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.threshold-card-container:hover {
|
||||
.edit-action-btns {
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.lightMode {
|
||||
.operator-input-root {
|
||||
color: var(--bg-ink-400);
|
||||
}
|
||||
.threshold-container {
|
||||
.threshold-card-container {
|
||||
border: 1px solid var(--bg-vanilla-300);
|
||||
background: var(--bg-vanilla-100);
|
||||
|
||||
.edit-action-btns {
|
||||
.ant-btn {
|
||||
background: var(--bg-vanilla-300);
|
||||
}
|
||||
|
||||
.ant-btn + .ant-btn {
|
||||
border-left: 1px solid var(--bg-vanilla-100);
|
||||
}
|
||||
}
|
||||
|
||||
.time-series-alerts {
|
||||
border: 1px solid var(--bg-vanilla-300);
|
||||
background: var(--bg-vanilla-100);
|
||||
|
||||
.label {
|
||||
color: var(--bg-ink-400);
|
||||
}
|
||||
|
||||
.label-input {
|
||||
border-left: 1px solid var(--bg-vanilla-300);
|
||||
background: var(--bg-vanilla-300);
|
||||
color: var(--bg-ink-300);
|
||||
border: 1px solid var(--bg-vanilla-300);
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
|
||||
.value-table-alerts {
|
||||
.operator-input {
|
||||
.ant-select-selector {
|
||||
background-color: var(--bg-vanilla-300) !important;
|
||||
color: var(--bg-ink-300);
|
||||
}
|
||||
}
|
||||
|
||||
.typography {
|
||||
color: var(--bg-ink-400);
|
||||
}
|
||||
.typography-preview {
|
||||
color: var(--bg-ink-400);
|
||||
border: 1px solid var(--bg-vanilla-300);
|
||||
background-color: var(--bg-vanilla-300) !important;
|
||||
}
|
||||
}
|
||||
.threshold-units-selector {
|
||||
border: 1px solid var(--bg-vanilla-300);
|
||||
background: var(--bg-vanilla-300);
|
||||
|
||||
.unit-input {
|
||||
color: var(--bg-ink-400);
|
||||
background: var(--bg-vanilla-300);
|
||||
}
|
||||
|
||||
.unit-selection {
|
||||
border-left: 1px solid var(--bg-vanilla-100);
|
||||
color: var(--bg-ink-400);
|
||||
|
||||
.ant-select-selector {
|
||||
background-color: var(--bg-vanilla-300);
|
||||
color: var(--bg-ink-400);
|
||||
}
|
||||
}
|
||||
|
||||
.unit-selection-prev {
|
||||
border-left: 1px solid var(--bg-vanilla-100);
|
||||
color: var(--bg-ink-400);
|
||||
background: var(--bg-vanilla-300);
|
||||
}
|
||||
}
|
||||
|
||||
.thresholds-color-selector {
|
||||
border: 1px solid var(--bg-vanilla-300);
|
||||
border-top: 1px solid var(--bg-vanilla-100);
|
||||
background: var(--bg-vanilla-300);
|
||||
|
||||
.color-selector {
|
||||
color: var(--bg-ink-300);
|
||||
background: var(--bg-vanilla-300);
|
||||
border: 1px solid var(--bg-vanilla-300);
|
||||
border-top: 1px solid var(--bg-vanilla-100);
|
||||
}
|
||||
.color-format {
|
||||
border-left: 1px solid var(--bg-vanilla-100);
|
||||
color: var(--bg-ink-300);
|
||||
|
||||
.ant-select-selector {
|
||||
color: var(--bg-ink-400);
|
||||
}
|
||||
}
|
||||
|
||||
.color-format-prev {
|
||||
border-left: 1px solid var(--bg-vanilla-100);
|
||||
color: var(--bg-ink-300);
|
||||
background: var(--bg-vanilla-300);
|
||||
}
|
||||
}
|
||||
|
||||
.threshold-action-button {
|
||||
.discard-btn {
|
||||
border: 1px solid var(--bg-vanilla-300);
|
||||
background: var(--bg-vanilla-300);
|
||||
}
|
||||
|
||||
.save-changes {
|
||||
color: var(--bg-vanilla-100);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,18 +1,10 @@
|
||||
/* eslint-disable sonarjs/cognitive-complexity */
|
||||
import './Threshold.styles.scss';
|
||||
|
||||
import { CheckOutlined, DeleteOutlined, EditOutlined } from '@ant-design/icons';
|
||||
import {
|
||||
Card,
|
||||
Divider,
|
||||
Input,
|
||||
InputNumber,
|
||||
Select,
|
||||
Space,
|
||||
Typography,
|
||||
} from 'antd';
|
||||
import { Button, Input, InputNumber, Select, Space, Typography } from 'antd';
|
||||
import { PANEL_TYPES } from 'constants/queryBuilder';
|
||||
import { useIsDarkMode } from 'hooks/useDarkMode';
|
||||
import { Check, Pencil, Trash2, X } from 'lucide-react';
|
||||
import { useRef, useState } from 'react';
|
||||
import { useDrag, useDrop, XYCoord } from 'react-dnd';
|
||||
|
||||
@ -29,6 +21,7 @@ import { ThresholdProps } from './types';
|
||||
|
||||
const wrapStyle = {
|
||||
flexWrap: 'wrap',
|
||||
gap: '10px',
|
||||
} as React.CSSProperties;
|
||||
|
||||
function Threshold({
|
||||
@ -90,6 +83,16 @@ function Threshold({
|
||||
);
|
||||
};
|
||||
|
||||
const discardHandler = (): void => {
|
||||
setIsEditMode(false);
|
||||
setOperator(thresholdOperator);
|
||||
setValue(thresholdValue);
|
||||
setUnit(thresholdUnit);
|
||||
setColor(thresholdColor);
|
||||
setFormat(thresholdFormat);
|
||||
setLabel(thresholdLabel);
|
||||
setTableSelectedOption(thresholdTableOptions);
|
||||
};
|
||||
const editHandler = (): void => {
|
||||
setIsEditMode(true);
|
||||
};
|
||||
@ -187,7 +190,6 @@ function Threshold({
|
||||
setLabel(event.target.value);
|
||||
};
|
||||
|
||||
const backgroundColor = !isDarkMode ? '#ffffff' : '#141414';
|
||||
const allowDragAndDrop = panelTypeVsDragAndDrop[selectedGraph];
|
||||
|
||||
return (
|
||||
@ -197,147 +199,165 @@ function Threshold({
|
||||
data-handler-id={handlerId}
|
||||
className="threshold-container"
|
||||
>
|
||||
<Card
|
||||
className={
|
||||
isDarkMode
|
||||
? `threshold-card threshold-card-dark`
|
||||
: `threshold-card threshold-card-light`
|
||||
}
|
||||
>
|
||||
<div className="threshold-card-container">
|
||||
<div className="threshold-action-button">
|
||||
{isEditMode ? (
|
||||
<CheckOutlined onClick={saveHandler} />
|
||||
) : (
|
||||
<EditOutlined className="threshold-action-icon" onClick={editHandler} />
|
||||
)}
|
||||
<Divider type="vertical" />
|
||||
<DeleteOutlined
|
||||
className="threshold-action-icon"
|
||||
<div className="threshold-card-container">
|
||||
{!isEditMode && (
|
||||
<div className="edit-action-btns">
|
||||
<Button
|
||||
type="text"
|
||||
icon={<Pencil size={14} />}
|
||||
className="edit-btn"
|
||||
onClick={editHandler}
|
||||
/>
|
||||
<Button
|
||||
type="text"
|
||||
icon={<Trash2 size={14} />}
|
||||
className="delete-btn"
|
||||
onClick={deleteHandler}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<Space
|
||||
direction={
|
||||
selectedGraph === PANEL_TYPES.TABLE ? 'vertical' : 'horizontal'
|
||||
}
|
||||
>
|
||||
{selectedGraph === PANEL_TYPES.TIME_SERIES && (
|
||||
<Space style={wrapStyle}>
|
||||
<Typography.Text>Label</Typography.Text>
|
||||
{isEditMode ? (
|
||||
<Input
|
||||
defaultValue={label}
|
||||
onChange={handleLabelChange}
|
||||
bordered={!isDarkMode}
|
||||
style={{ backgroundColor }}
|
||||
/>
|
||||
) : (
|
||||
<ShowCaseValue width="180px" value={label || 'none'} />
|
||||
)}
|
||||
</Space>
|
||||
)}
|
||||
<div style={{ width: '100%' }}>
|
||||
{selectedGraph === PANEL_TYPES.TIME_SERIES && (
|
||||
<div className="time-series-alerts">
|
||||
<Typography.Text className="label">Label</Typography.Text>
|
||||
{isEditMode ? (
|
||||
<Input
|
||||
defaultValue={label}
|
||||
onChange={handleLabelChange}
|
||||
bordered={!isDarkMode}
|
||||
className="label-input"
|
||||
/>
|
||||
) : (
|
||||
<ShowCaseValue value={label || 'none'} className="label-input" />
|
||||
)}
|
||||
{(selectedGraph === PANEL_TYPES.VALUE ||
|
||||
selectedGraph === PANEL_TYPES.TABLE) && (
|
||||
<>
|
||||
<Typography.Text>
|
||||
If value {selectedGraph === PANEL_TYPES.TABLE ? 'in' : 'is'}
|
||||
</Typography.Text>
|
||||
{isEditMode ? (
|
||||
<>
|
||||
{selectedGraph === PANEL_TYPES.TABLE && (
|
||||
<Space style={wrapStyle}>
|
||||
<Select
|
||||
style={{
|
||||
minWidth: '150px',
|
||||
backgroundColor,
|
||||
borderRadius: '5px',
|
||||
}}
|
||||
defaultValue={tableSelectedOption}
|
||||
options={tableOptions}
|
||||
bordered={!isDarkMode}
|
||||
showSearch
|
||||
onChange={handleTableOptionsChange}
|
||||
/>
|
||||
<Typography.Text>is</Typography.Text>
|
||||
</Space>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
{(selectedGraph === PANEL_TYPES.VALUE ||
|
||||
selectedGraph === PANEL_TYPES.TABLE) && (
|
||||
<div className="value-table-alerts">
|
||||
<Typography.Text className="typography">
|
||||
If value {selectedGraph === PANEL_TYPES.TABLE ? 'in' : 'is'}
|
||||
</Typography.Text>
|
||||
{isEditMode ? (
|
||||
<div>
|
||||
{selectedGraph === PANEL_TYPES.TABLE && (
|
||||
<Space style={wrapStyle}>
|
||||
<Select
|
||||
style={{ minWidth: '73px', backgroundColor }}
|
||||
defaultValue={operator}
|
||||
options={operatorOptions}
|
||||
onChange={handleOperatorChange}
|
||||
defaultValue={tableSelectedOption}
|
||||
options={tableOptions}
|
||||
bordered={!isDarkMode}
|
||||
showSearch
|
||||
onChange={handleTableOptionsChange}
|
||||
rootClassName="operator-input-root"
|
||||
className="operator-input"
|
||||
/>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
{selectedGraph === PANEL_TYPES.TABLE && (
|
||||
<Space style={wrapStyle}>
|
||||
<ShowCaseValue width="150px" value={tableSelectedOption} />
|
||||
<Typography.Text>is</Typography.Text>
|
||||
</Space>
|
||||
)}
|
||||
<ShowCaseValue width="49px" value={operator} />
|
||||
</>
|
||||
<Typography.Text className="typography">is</Typography.Text>
|
||||
</Space>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</Space>
|
||||
</div>
|
||||
<div className="threshold-units-selector">
|
||||
<Space style={wrapStyle}>
|
||||
{isEditMode ? (
|
||||
<InputNumber
|
||||
style={{ backgroundColor }}
|
||||
defaultValue={value}
|
||||
onChange={handleValueChange}
|
||||
bordered={!isDarkMode}
|
||||
/>
|
||||
<Select
|
||||
defaultValue={operator}
|
||||
options={operatorOptions}
|
||||
onChange={handleOperatorChange}
|
||||
bordered={!isDarkMode}
|
||||
style={{ marginLeft: '10px' }}
|
||||
rootClassName="operator-input-root"
|
||||
className="operator-input"
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<ShowCaseValue width="60px" value={value} />
|
||||
<div>
|
||||
{selectedGraph === PANEL_TYPES.TABLE && (
|
||||
<Space>
|
||||
<ShowCaseValue
|
||||
value={tableSelectedOption}
|
||||
className="typography-preview"
|
||||
/>
|
||||
<Typography.Text
|
||||
className="typography"
|
||||
style={{ marginRight: '10px' }}
|
||||
>
|
||||
is
|
||||
</Typography.Text>
|
||||
</Space>
|
||||
)}
|
||||
<ShowCaseValue
|
||||
width="50px"
|
||||
value={operator}
|
||||
className="typography-preview"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{isEditMode ? (
|
||||
<Select
|
||||
style={{ minWidth: '200px', backgroundColor }}
|
||||
bordered={!isDarkMode}
|
||||
defaultValue={unit}
|
||||
options={unitOptions}
|
||||
onChange={handleUnitChange}
|
||||
showSearch
|
||||
/>
|
||||
) : (
|
||||
<ShowCaseValue width="200px" value={unit} />
|
||||
)}
|
||||
</Space>
|
||||
</div>
|
||||
<div>
|
||||
<Space direction="vertical">
|
||||
<Typography.Text>Show with</Typography.Text>
|
||||
<Space style={wrapStyle}>
|
||||
{isEditMode ? (
|
||||
<>
|
||||
<ColorSelector setColor={setColor} thresholdColor={color} />
|
||||
<Select
|
||||
style={{ minWidth: '100px', backgroundColor }}
|
||||
defaultValue={format}
|
||||
options={showAsOptions}
|
||||
onChange={handlerFormatChange}
|
||||
bordered={!isDarkMode}
|
||||
/>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<ShowCaseValue width="120px" value={<CustomColor color={color} />} />
|
||||
<ShowCaseValue width="100px" value={format} />
|
||||
</>
|
||||
)}
|
||||
</Space>
|
||||
</Space>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</Card>
|
||||
<div className="threshold-units-selector">
|
||||
{isEditMode ? (
|
||||
<InputNumber
|
||||
defaultValue={value}
|
||||
onChange={handleValueChange}
|
||||
className="unit-input"
|
||||
/>
|
||||
) : (
|
||||
<ShowCaseValue value={value} className="unit-input" />
|
||||
)}
|
||||
{isEditMode ? (
|
||||
<Select
|
||||
defaultValue={unit}
|
||||
options={unitOptions}
|
||||
onChange={handleUnitChange}
|
||||
showSearch
|
||||
className="unit-selection"
|
||||
/>
|
||||
) : (
|
||||
<ShowCaseValue value={unit} className="unit-selection-prev" />
|
||||
)}
|
||||
</div>
|
||||
<div className="thresholds-color-selector">
|
||||
{isEditMode ? (
|
||||
<>
|
||||
<div className="color-selector">
|
||||
<ColorSelector setColor={setColor} thresholdColor={color} />
|
||||
</div>
|
||||
<Select
|
||||
defaultValue={format}
|
||||
options={showAsOptions}
|
||||
onChange={handlerFormatChange}
|
||||
rootClassName="color-format"
|
||||
/>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<ShowCaseValue
|
||||
value={<CustomColor color={color} />}
|
||||
className="color-selector"
|
||||
/>
|
||||
<ShowCaseValue
|
||||
width="100px"
|
||||
value={format}
|
||||
className="color-format-prev"
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
{isEditMode && (
|
||||
<div className="threshold-action-button">
|
||||
<Button
|
||||
className="discard-btn"
|
||||
icon={<X size={14} />}
|
||||
onClick={discardHandler}
|
||||
>
|
||||
Discard
|
||||
</Button>
|
||||
<Button
|
||||
className="save-changes"
|
||||
icon={<Check size={14} />}
|
||||
onClick={saveHandler}
|
||||
>
|
||||
Save Changes
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -1,18 +1,54 @@
|
||||
.threshold-selector-container {
|
||||
.threshold-selector-button {
|
||||
margin-top: 20px;
|
||||
width: 100%;
|
||||
border-radius: 10px;
|
||||
padding: 10px 0;
|
||||
height: 50px;
|
||||
border-color: #1C64F2;
|
||||
color: #1C64F2;
|
||||
|
||||
span {
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
line-height: 22px;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
padding: 12px;
|
||||
padding-bottom: 80px;
|
||||
|
||||
.threshold-select {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
cursor: pointer;
|
||||
|
||||
.icon {
|
||||
color: var(--bg-vanilla-400);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.left-section {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
|
||||
.icon {
|
||||
color: var(--bg-vanilla-400);
|
||||
}
|
||||
.text {
|
||||
color: var(--bg-vanilla-400);
|
||||
font-family: Inter;
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 20px; /* 142.857% */
|
||||
letter-spacing: 0.14px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.lightMode {
|
||||
.threshold-selector-container {
|
||||
.threshold-select {
|
||||
.icon {
|
||||
color: var(--bg-ink-400);
|
||||
}
|
||||
|
||||
.left-section {
|
||||
.icon {
|
||||
color: var(--bg-ink-400);
|
||||
}
|
||||
.text {
|
||||
color: var(--bg-ink-400);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,12 @@
|
||||
/* eslint-disable jsx-a11y/no-static-element-interactions */
|
||||
/* eslint-disable jsx-a11y/click-events-have-key-events */
|
||||
import './ThresholdSelector.styles.scss';
|
||||
|
||||
import { Button, Typography } from 'antd';
|
||||
import { Typography } from 'antd';
|
||||
import { ColumnsType } from 'antd/es/table';
|
||||
import { Events } from 'constants/events';
|
||||
import { RowData } from 'lib/query/createTableColumnsFromQuery';
|
||||
import { Antenna, Plus } from 'lucide-react';
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
import { DndProvider } from 'react-dnd';
|
||||
import { HTML5Backend } from 'react-dnd-html5-backend';
|
||||
@ -52,7 +55,6 @@ function ThresholdSelector({
|
||||
|
||||
const addThresholdHandler = (): void => {
|
||||
setThresholds([
|
||||
...thresholds,
|
||||
{
|
||||
index: uuid(),
|
||||
isEditEnabled: true,
|
||||
@ -66,6 +68,7 @@ function ThresholdSelector({
|
||||
selectedGraph,
|
||||
thresholdTableOptions: tableOptions[0]?.value || '',
|
||||
},
|
||||
...thresholds,
|
||||
]);
|
||||
};
|
||||
|
||||
@ -79,7 +82,13 @@ function ThresholdSelector({
|
||||
return (
|
||||
<DndProvider backend={HTML5Backend}>
|
||||
<div className="threshold-selector-container">
|
||||
<Typography.Text>Thresholds</Typography.Text>
|
||||
<div className="threshold-select" onClick={addThresholdHandler}>
|
||||
<div className="left-section">
|
||||
<Antenna size={14} className="icon" />
|
||||
<Typography.Text className="text">Thresholds</Typography.Text>
|
||||
</div>
|
||||
<Plus size={14} onClick={addThresholdHandler} className="icon" />
|
||||
</div>
|
||||
{thresholds.map((threshold, idx) => (
|
||||
<Threshold
|
||||
key={threshold.index}
|
||||
@ -100,9 +109,6 @@ function ThresholdSelector({
|
||||
thresholdTableOptions={threshold.thresholdTableOptions}
|
||||
/>
|
||||
))}
|
||||
<Button className="threshold-selector-button" onClick={addThresholdHandler}>
|
||||
+ Add threshold
|
||||
</Button>
|
||||
</div>
|
||||
</DndProvider>
|
||||
);
|
||||
|
@ -22,8 +22,9 @@ export type ThresholdProps = {
|
||||
};
|
||||
|
||||
export type ShowCaseValueProps = {
|
||||
width: string;
|
||||
width?: string;
|
||||
value: ReactNode;
|
||||
className?: string;
|
||||
};
|
||||
|
||||
export type CustomColorProps = {
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { AutoComplete, Col, Input, Typography } from 'antd';
|
||||
import { AutoComplete, Input, Typography } from 'antd';
|
||||
import { find } from 'lodash-es';
|
||||
import { Dispatch, SetStateAction } from 'react';
|
||||
|
||||
@ -29,10 +29,11 @@ function YAxisUnitSelector({
|
||||
value: options.name,
|
||||
}));
|
||||
return (
|
||||
<Col style={{ marginBottom: 12, marginTop: 12 }}>
|
||||
<Typography.Text>{fieldLabel}</Typography.Text>
|
||||
<div className="y-axis-unit-selector">
|
||||
<Typography.Text className="heading">{fieldLabel}</Typography.Text>
|
||||
<AutoComplete
|
||||
style={{ width: '100%' }}
|
||||
rootClassName="y-axis-root-popover"
|
||||
options={options}
|
||||
defaultValue={findCategoryById(defaultValue)?.name}
|
||||
onSelect={onSelectHandler}
|
||||
@ -45,9 +46,9 @@ function YAxisUnitSelector({
|
||||
return false;
|
||||
}}
|
||||
>
|
||||
<Input size="large" placeholder="Unit" allowClear />
|
||||
<Input placeholder="Unit" allowClear rootClassName="input" />
|
||||
</AutoComplete>
|
||||
</Col>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -1,15 +1,8 @@
|
||||
import { UploadOutlined } from '@ant-design/icons';
|
||||
import {
|
||||
Button,
|
||||
Divider,
|
||||
Input,
|
||||
InputNumber,
|
||||
Select,
|
||||
Space,
|
||||
Switch,
|
||||
Typography,
|
||||
} from 'antd';
|
||||
import InputComponent from 'components/Input';
|
||||
/* eslint-disable jsx-a11y/no-static-element-interactions */
|
||||
/* eslint-disable jsx-a11y/click-events-have-key-events */
|
||||
import './RightContainer.styles.scss';
|
||||
|
||||
import { Input, InputNumber, Select, Space, Switch, Typography } from 'antd';
|
||||
import TimePreference from 'components/TimePreferenceDropDown';
|
||||
import { PANEL_TYPES } from 'constants/queryBuilder';
|
||||
import GraphTypes, {
|
||||
@ -17,6 +10,7 @@ import GraphTypes, {
|
||||
} from 'container/NewDashboard/ComponentsSlider/menuItems';
|
||||
import useCreateAlerts from 'hooks/queryBuilder/useCreateAlerts';
|
||||
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
|
||||
import { ConciergeBell, Plus } from 'lucide-react';
|
||||
import {
|
||||
Dispatch,
|
||||
SetStateAction,
|
||||
@ -35,7 +29,6 @@ import {
|
||||
panelTypeVsThreshold,
|
||||
panelTypeVsYAxisUnit,
|
||||
} from './constants';
|
||||
import { Container, Title } from './styles';
|
||||
import ThresholdSelector from './Threshold/ThresholdSelector';
|
||||
import { ThresholdProps } from './Threshold/types';
|
||||
import { timePreferance } from './timeItems';
|
||||
@ -118,67 +111,72 @@ function RightContainer({
|
||||
);
|
||||
|
||||
return (
|
||||
<Container>
|
||||
<Title>Panel Type</Title>
|
||||
<Select
|
||||
onChange={setGraphHandler}
|
||||
value={selectedGraph}
|
||||
style={{ width: '100%', marginBottom: 24 }}
|
||||
>
|
||||
{graphTypes.map((item) => (
|
||||
<Option key={item.name} value={item.name}>
|
||||
{item.display}
|
||||
</Option>
|
||||
))}
|
||||
</Select>
|
||||
<Title>Panel Attributes</Title>
|
||||
<div className="right-container">
|
||||
<section className="header">
|
||||
<div className="purple-dot" />
|
||||
<Typography.Text className="header-text">Panel details</Typography.Text>
|
||||
</section>
|
||||
<section className="name-description">
|
||||
<Typography.Text className="typography">Name</Typography.Text>
|
||||
<Input
|
||||
placeholder="Enter the panel name here..."
|
||||
onChange={(event): void => onChangeHandler(setTitle, event.target.value)}
|
||||
value={title}
|
||||
rootClassName="name-input"
|
||||
/>
|
||||
<Typography.Text className="typography">Description</Typography.Text>
|
||||
<TextArea
|
||||
placeholder="Enter the panel description here..."
|
||||
bordered
|
||||
allowClear
|
||||
value={description}
|
||||
onChange={(event): void =>
|
||||
onChangeHandler(setDescription, event.target.value)
|
||||
}
|
||||
rootClassName="description-input"
|
||||
/>
|
||||
</section>
|
||||
<section className="panel-config">
|
||||
<Typography.Text className="typography">Panel Type</Typography.Text>
|
||||
<Select
|
||||
onChange={setGraphHandler}
|
||||
value={selectedGraph}
|
||||
style={{ width: '100%' }}
|
||||
className="panel-type-select"
|
||||
>
|
||||
{graphTypes.map((item) => (
|
||||
<Option key={item.name} value={item.name}>
|
||||
<div className="select-option">
|
||||
<div className="icon">{item.icon}</div>
|
||||
<Typography.Text className="display">{item.display}</Typography.Text>
|
||||
</div>
|
||||
</Option>
|
||||
))}
|
||||
</Select>
|
||||
|
||||
<InputComponent
|
||||
label="Panel Title"
|
||||
size="middle"
|
||||
placeholder="Title"
|
||||
labelOnTop
|
||||
onChangeHandler={(event): void =>
|
||||
onChangeHandler(setTitle, event.target.value)
|
||||
}
|
||||
value={title}
|
||||
/>
|
||||
{allowFillSpans && (
|
||||
<Space className="fill-gaps">
|
||||
<Typography className="fill-gaps-text">Fill gaps</Typography>
|
||||
<Switch
|
||||
checked={isFillSpans}
|
||||
size="small"
|
||||
onChange={(checked): void => setIsFillSpans(checked)}
|
||||
/>
|
||||
</Space>
|
||||
)}
|
||||
|
||||
<Title light="true">Description</Title>
|
||||
|
||||
<TextArea
|
||||
placeholder="Write something describing the panel"
|
||||
bordered
|
||||
allowClear
|
||||
value={description}
|
||||
onChange={(event): void =>
|
||||
onChangeHandler(setDescription, event.target.value)
|
||||
}
|
||||
/>
|
||||
|
||||
{allowFillSpans && (
|
||||
<Space style={{ marginTop: 10 }} direction="vertical">
|
||||
<Typography>Fill gaps</Typography>
|
||||
|
||||
<Switch
|
||||
checked={isFillSpans}
|
||||
onChange={(checked): void => setIsFillSpans(checked)}
|
||||
/>
|
||||
</Space>
|
||||
)}
|
||||
|
||||
{allowPanelTimePreference && (
|
||||
<Title light="true">Panel Time Preference</Title>
|
||||
)}
|
||||
|
||||
<Space direction="vertical">
|
||||
{allowPanelTimePreference && (
|
||||
<TimePreference
|
||||
{...{
|
||||
selectedTime,
|
||||
setSelectedTime,
|
||||
}}
|
||||
/>
|
||||
<>
|
||||
<Typography.Text className="panel-time-text">
|
||||
Panel Time Preference
|
||||
</Typography.Text>
|
||||
<TimePreference
|
||||
{...{
|
||||
selectedTime,
|
||||
setSelectedTime,
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
|
||||
{allowYAxisUnit && (
|
||||
@ -188,50 +186,51 @@ function RightContainer({
|
||||
fieldLabel={selectedGraphType === 'Value' ? 'Unit' : 'Y Axis Unit'}
|
||||
/>
|
||||
)}
|
||||
|
||||
{allowCreateAlerts && (
|
||||
<Button icon={<UploadOutlined />} onClick={onCreateAlertsHandler}>
|
||||
Create Alerts from Queries
|
||||
</Button>
|
||||
{allowSoftMinMax && (
|
||||
<section className="soft-min-max">
|
||||
<section className="container">
|
||||
<Typography.Text className="text">Soft Min</Typography.Text>
|
||||
<InputNumber
|
||||
type="number"
|
||||
value={softMin}
|
||||
onChange={softMinHandler}
|
||||
rootClassName="input"
|
||||
/>
|
||||
</section>
|
||||
<section className="container">
|
||||
<Typography.Text className="text">Soft Max</Typography.Text>
|
||||
<InputNumber
|
||||
value={softMax}
|
||||
type="number"
|
||||
rootClassName="input"
|
||||
onChange={softMaxHandler}
|
||||
/>
|
||||
</section>
|
||||
</section>
|
||||
)}
|
||||
</Space>
|
||||
</section>
|
||||
|
||||
{allowSoftMinMax && (
|
||||
<>
|
||||
<Divider />
|
||||
<Typography.Text style={{ display: 'block', margin: '5px 0' }}>
|
||||
Soft Min
|
||||
</Typography.Text>
|
||||
<InputNumber
|
||||
type="number"
|
||||
value={softMin}
|
||||
style={{ display: 'block', width: '100%' }}
|
||||
onChange={softMinHandler}
|
||||
/>
|
||||
<Typography.Text style={{ display: 'block', margin: '5px 0' }}>
|
||||
Soft Max
|
||||
</Typography.Text>
|
||||
<InputNumber
|
||||
value={softMax}
|
||||
type="number"
|
||||
style={{ display: 'block', width: '100%' }}
|
||||
onChange={softMaxHandler}
|
||||
/>
|
||||
</>
|
||||
{allowCreateAlerts && (
|
||||
<section className="alerts" onClick={onCreateAlertsHandler}>
|
||||
<div className="left-section">
|
||||
<ConciergeBell size={14} className="bell-icon" />
|
||||
<Typography.Text className="alerts-text">Alerts</Typography.Text>
|
||||
</div>
|
||||
<Plus size={14} className="plus-icon" />
|
||||
</section>
|
||||
)}
|
||||
|
||||
{allowThreshold && (
|
||||
<>
|
||||
<Divider />
|
||||
<section>
|
||||
<ThresholdSelector
|
||||
thresholds={thresholds}
|
||||
setThresholds={setThresholds}
|
||||
yAxisUnit={yAxisUnit}
|
||||
selectedGraph={selectedGraph}
|
||||
/>
|
||||
</>
|
||||
</section>
|
||||
)}
|
||||
</Container>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user