UI fixes and updates for dashboards and data source CRUD v2 (#2939)
* fixes for beta 8 Signed-off-by: ishangupta-ds <ishan@chaosnative.com> * frontend fixes Signed-off-by: ishangupta-ds <ishan@chaosnative.com> * fixes Signed-off-by: ishangupta-ds <ishan@chaosnative.com> * fixes and updates Signed-off-by: ishangupta-ds <ishan@chaosnative.com> * updates and fixes Signed-off-by: ishangupta-ds <ishan@chaosnative.com> * fixes Signed-off-by: ishangupta-ds <ishan@chaosnative.com> * fixed deep scan issues Signed-off-by: ishangupta-ds <ishan@chaosnative.com> * 0px to 0 and sock shop icon update Signed-off-by: ishangupta-ds <ishan@chaosnative.com> * minor fix after review Signed-off-by: ishangupta-ds <ishan@chaosnative.com> * fixes to translation and condition Signed-off-by: ishangupta-ds <ishan@chaosnative.com>
|
@ -0,0 +1,3 @@
|
|||
<svg width="38" height="38" viewBox="0 0 38 38" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path opacity="0.7" d="M33.918 24.6262C32.0531 24.6262 30.4775 25.885 29.9915 27.5981H26.0508V23.2475H30.0744C34.4446 23.2475 38 19.6883 38 15.3134C38 11.8056 35.6761 8.72413 32.3769 7.7205C31.3796 3.72291 27.775 0.855778 23.6002 0.855778C21.9819 0.855778 20.4316 1.27356 19.0635 2.07176C17.6145 0.74522 15.7146 0 13.7183 0C9.43639 0 5.93668 3.41709 5.797 7.67012C2.4081 8.6184 0 11.7246 0 15.3134C0 19.6883 3.55538 23.2475 7.9256 23.2475H11.9492V27.5981H8.0085C7.52259 25.885 5.94693 24.6262 4.08203 24.6262C1.8312 24.6262 0 26.4593 0 28.7126C0 30.9659 1.8312 32.7991 4.08203 32.7991C5.94693 32.7991 7.52252 31.5402 8.0085 29.8271H13.0625C13.6773 29.8271 14.1758 29.3281 14.1758 28.7126V23.2475H17.8867V29.9828C16.1755 30.4693 14.918 32.0466 14.918 33.9135C14.918 36.1668 16.7492 38 19 38C21.2508 38 23.082 36.1668 23.082 33.9135C23.082 32.0466 21.8245 30.4693 20.1133 29.9828V23.2475H23.8242V28.7126C23.8242 29.3281 24.3227 29.8271 24.9375 29.8271H29.9915C30.4774 31.5402 32.0531 32.7991 33.918 32.7991C36.1688 32.7991 38 30.9659 38 28.7126C38 26.4593 36.1688 24.6262 33.918 24.6262ZM4.08203 30.5701C3.05893 30.5701 2.22656 29.7368 2.22656 28.7126C2.22656 27.6884 3.05893 26.8551 4.08203 26.8551C5.10514 26.8551 5.9375 27.6884 5.9375 28.7126C5.9375 29.7368 5.10514 30.5701 4.08203 30.5701ZM20.8555 33.9135C20.8555 34.9378 20.0231 35.771 19 35.771C17.9769 35.771 17.1445 34.9378 17.1445 33.9135C17.1445 32.8893 17.9769 32.0561 19 32.0561C20.0231 32.0561 20.8555 32.8893 20.8555 33.9135ZM7.9256 21.0186C4.78318 21.0186 2.22656 18.4593 2.22656 15.3134C2.22656 12.5016 4.32005 10.0748 7.09606 9.6684C7.68186 9.58266 8.09808 9.05291 8.04346 8.46282C8.02735 8.28874 8.01919 8.11087 8.01919 7.93411C8.01919 4.78836 10.5758 2.22905 13.7182 2.22905C15.3948 2.22905 16.9759 2.95867 18.0559 4.23082C18.4351 4.67743 19.095 4.75366 19.5659 4.40534C20.7335 3.54139 22.1285 3.08475 23.6002 3.08475C26.9866 3.08475 29.8028 5.57221 30.3293 8.80801C30.4037 9.26555 30.7534 9.62917 31.2072 9.72115C33.853 10.2574 35.7734 12.6092 35.7734 15.3134C35.7734 18.4593 33.2168 21.0186 30.0743 21.0186H7.9256ZM33.918 30.5701C32.8949 30.5701 32.0625 29.7368 32.0625 28.7126C32.0625 27.6884 32.8949 26.8551 33.918 26.8551C34.9411 26.8551 35.7734 27.6884 35.7734 28.7126C35.7734 29.7368 34.9411 30.5701 33.918 30.5701Z" fill="#DBA017"/>
|
||||
</svg>
|
After Width: | Height: | Size: 2.4 KiB |
|
@ -0,0 +1,5 @@
|
|||
<svg width="42" height="21" viewBox="0 0 42 21" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect x="19.4121" y="7.00098" width="5.64705" height="6.65" fill="#DBA017"/>
|
||||
<path d="M20.1764 10.5C20.1764 15.7388 15.8915 20 10.5882 20C5.28488 20 1 15.7388 1 10.5C1 5.26117 5.28488 1 10.5882 1C15.8915 1 20.1764 5.26117 20.1764 10.5Z" stroke="#DBA017" stroke-width="2"/>
|
||||
<rect x="29.2939" y="7.00098" width="12.7059" height="6.65" fill="#DBA017"/>
|
||||
</svg>
|
After Width: | Height: | Size: 453 B |
|
@ -1,6 +1,6 @@
|
|||
<svg width="63" height="65" viewBox="0 0 63 65" fill="none" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<rect y="0.5" width="63" height="64" rx="31" fill="white"/>
|
||||
<rect y="0.5" width="63" height="64" rx="31" fill="url(#pattern0)"/>
|
||||
<svg width="19" height="19" viewBox="0 0 19 19" fill="none" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<rect width="19" height="19" rx="9.5" fill="white"/>
|
||||
<rect width="19" height="19" rx="9.5" fill="url(#pattern0)"/>
|
||||
<defs>
|
||||
<pattern id="pattern0" patternContentUnits="objectBoundingBox" width="1" height="1">
|
||||
<use xlink:href="#image0" transform="translate(-0.00969232 -0.00989646) scale(0.000538194)"/>
|
||||
|
|
Before Width: | Height: | Size: 127 KiB After Width: | Height: | Size: 127 KiB |
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 192 KiB |
|
@ -0,0 +1,6 @@
|
|||
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M7.11642 7.00293H4.88667C4.70008 7.00293 4.54883 7.15256 4.54883 7.33716V11.6654C4.54883 11.85 4.70008 11.9996 4.88667 11.9996H7.11642C7.30301 11.9996 7.45427 11.85 7.45427 11.6654V7.33716C7.45427 7.15256 7.30301 7.00293 7.11642 7.00293ZM6.77858 11.3312H5.22451V7.67139H6.77858V11.3312Z" fill="#1C0732"/>
|
||||
<path d="M11.5415 4.37744H9.29487C9.10828 4.37744 8.95703 4.52708 8.95703 4.71167V11.6636C8.95703 11.8482 9.10828 11.9979 9.29487 11.9979H11.5415C11.7281 11.9979 11.8794 11.8482 11.8794 11.6636V4.71167C11.8794 4.52711 11.7281 4.37744 11.5415 4.37744ZM11.2037 11.3294H9.63272V5.0459H11.2037V11.3294Z" fill="#1C0732"/>
|
||||
<path d="M2.70755 8.62451H0.46089C0.274299 8.62451 0.123047 8.77415 0.123047 8.95874V11.666C0.123047 11.8506 0.274299 12.0002 0.46089 12.0002H2.70755C2.89414 12.0002 3.04539 11.8506 3.04539 11.666V8.95874C3.04539 8.77418 2.89414 8.62451 2.70755 8.62451ZM2.36971 11.3318H0.798732V9.29297H2.36971V11.3318Z" fill="#1C0732"/>
|
||||
<path d="M11.9129 0.335185C11.8615 0.265134 11.7807 0.222079 11.6933 0.218209L8.61893 0.000954961C8.43233 -0.0128953 8.26973 0.125543 8.25573 0.310107C8.24173 0.494702 8.38164 0.655564 8.56823 0.669414L10.7441 0.815593L6.7439 3.91146L3.68643 1.53843C3.55587 1.43614 3.36948 1.44324 3.24723 1.55513L0.105313 4.52974C-0.0282527 4.65509 -0.0357539 4.8632 0.0884354 4.99767C0.148703 5.07505 0.243179 5.11867 0.341826 5.11464C0.430934 5.1134 0.515936 5.07735 0.578306 5.01437L3.50065 2.24025L6.52433 4.59656C6.64774 4.69431 6.82321 4.69431 6.94662 4.59656L11.2711 1.26744L11.1021 3.34325C11.1012 3.52474 11.2404 3.6769 11.423 3.69418H11.4399C11.6141 3.69504 11.7603 3.56479 11.7778 3.39337L11.9974 0.569136C12.0097 0.482164 11.9782 0.394712 11.9129 0.335185Z" fill="#1C0732"/>
|
||||
</svg>
|
After Width: | Height: | Size: 1.8 KiB |
|
@ -25,7 +25,7 @@ getStarted:
|
|||
|
||||
sidebar:
|
||||
title: Litmus
|
||||
version: 'Version: 1.13'
|
||||
version: "Version: 1.13"
|
||||
|
||||
header:
|
||||
projectDropdown:
|
||||
|
@ -34,7 +34,7 @@ header:
|
|||
otherProjects: Other Projects
|
||||
noProjectsOther: You are not part of any projects not owned by you
|
||||
profileDropdown:
|
||||
signedIn: 'Signed in as:'
|
||||
signedIn: "Signed in as:"
|
||||
emailUnset: Email not set
|
||||
emailSet: Set up your email
|
||||
switchProject: Please switch to a project you own to access settings
|
||||
|
@ -53,8 +53,8 @@ quickActionCard:
|
|||
quickSurvey: Take a quick survey
|
||||
readDocs: Read the Litmus docs
|
||||
readAPIDocs: Read the Litmus Portal API docs
|
||||
addDashboard: Add a Dashboard
|
||||
addDataSource: Add a Data Source
|
||||
addDashboard: Create a dashboard
|
||||
addDataSource: Add a data source
|
||||
addAgent: Add an agent
|
||||
connectNewAgent: Connect a new agent
|
||||
scheduleWorkflow: Schedule a workflow
|
||||
|
@ -89,7 +89,7 @@ workflowStepper:
|
|||
back: Back
|
||||
|
||||
editor:
|
||||
status: 'YAML Status:'
|
||||
status: "YAML Status:"
|
||||
|
||||
######################################
|
||||
############ Pages #############
|
||||
|
@ -107,14 +107,14 @@ login:
|
|||
tooltipText: You need to contact your admin to reset your password.
|
||||
|
||||
schedule:
|
||||
heading: 'Schedule a workflow'
|
||||
headingDesc: 'Click on test to see detailed log of your workflow'
|
||||
headingText: 'Schedule details:'
|
||||
description: 'Choose the right time to schedule your first workflow. Below you can find some options that may be convenient for you.'
|
||||
save: 'Save Changes'
|
||||
heading: "Schedule a workflow"
|
||||
headingDesc: "Click on test to see detailed log of your workflow"
|
||||
headingText: "Schedule details:"
|
||||
description: "Choose the right time to schedule your first workflow. Below you can find some options that may be convenient for you."
|
||||
save: "Save Changes"
|
||||
scheduleNow: Schedule now
|
||||
scheduleAfter: Schedule after some time
|
||||
scheduleAfterSometime: 'Choose the minutes, hours, or days when you want to start workflow'
|
||||
scheduleAfterSometime: "Choose the minutes, hours, or days when you want to start workflow"
|
||||
scheduleSpecificTime: Schedule at a specific time
|
||||
scheduleFuture: Select date and time to start workflow in future
|
||||
scheduleRecurring: Recurring Schedule
|
||||
|
@ -122,7 +122,7 @@ schedule:
|
|||
after: After
|
||||
at: at
|
||||
At: At
|
||||
scheduleOn: 'On'
|
||||
scheduleOn: "On"
|
||||
missingPerm: Missing sufficient permissions :(
|
||||
requiredPerm: Looks like you do not have the required permission to create a new workflow on this project.
|
||||
contact: Contact portal administrator to upgrade your permission.
|
||||
|
@ -143,11 +143,11 @@ editSchedule:
|
|||
successfullyCreated: is successfully created
|
||||
|
||||
community:
|
||||
title: 'Community'
|
||||
heading: 'Litmus Insights'
|
||||
headingDesc: 'Stats for the Litmus community'
|
||||
analyticDesc: 'Periodic growth of Litmus'
|
||||
statsHeading: 'Litmus users around the world:'
|
||||
title: "Community"
|
||||
heading: "Litmus Insights"
|
||||
headingDesc: "Stats for the Litmus community"
|
||||
analyticDesc: "Periodic growth of Litmus"
|
||||
statsHeading: "Litmus users around the world:"
|
||||
litmusChaos: Litmuschaos
|
||||
follow: Follow
|
||||
|
||||
|
@ -158,8 +158,8 @@ analytics:
|
|||
comingSoon: Analytics Coming Soon !
|
||||
waitingMessage: Waiting for workflow to start running !
|
||||
chaosCompleteWaitingMessage: Waiting for chaos experiment to finish !
|
||||
highestScore: 'Highest Score:'
|
||||
lowestScore: 'Lowest Score:'
|
||||
highestScore: "Highest Score:"
|
||||
lowestScore: "Lowest Score:"
|
||||
exportPDF: Export PDF
|
||||
compareWorkflows: Compare workflows
|
||||
targetCluster: Target cluster
|
||||
|
@ -200,6 +200,7 @@ analyticsDashboard:
|
|||
subHeading1: Dashboard Configuration Details
|
||||
subHeading2: Selected Applications
|
||||
subHeading3: Selected Metrics
|
||||
infoKeyNamespace: Namespace
|
||||
metaData1: Name
|
||||
metaData2: Type
|
||||
metaData3: DataSource
|
||||
|
@ -221,12 +222,12 @@ analyticsDashboard:
|
|||
every2Hours: Every 2 hours
|
||||
every1Day: Every 1 day
|
||||
chaosTable:
|
||||
tableHead1: Legend
|
||||
tableHead1: Chaos result name
|
||||
tableHead2: Workflow
|
||||
tableHead3: Experiment
|
||||
tableHead4: "Target \n ( namespace / pod )"
|
||||
tableHead3: Engine context
|
||||
tableHead4: Target
|
||||
tableHead5:
|
||||
title: Result
|
||||
title: Verdict
|
||||
infoText: Latest Chaos experiment verdict
|
||||
noRecords: No chaos happened during the interval
|
||||
showTable: Show Chaos during this interval
|
||||
|
@ -264,14 +265,41 @@ analyticsDashboard:
|
|||
workflowScheduleTable:
|
||||
title: Workflow Schedules
|
||||
noRecordMessage: No workflow has been schedule
|
||||
kubernetesDashboardTable:
|
||||
title: Application Dashboard
|
||||
noRecordMessage: No dashboard has been configured
|
||||
workflowDashboard: Workflow Dashboards
|
||||
workflowDashboard: Workflow dashboards
|
||||
overviewTabdataSourceTable: Connected Data Sources
|
||||
applicationDashboard: Application Dashboards
|
||||
applicationDashboard: Application dashboards
|
||||
applicationDashboardTable:
|
||||
warning:
|
||||
noActiveDataSource: No active data source found for creating an application dashboard.
|
||||
noAvailableDataSource: No data source available. To create an Application dashboard you need to add a data source.
|
||||
configureExisting: Configure an existing data source
|
||||
addNew: Add data source
|
||||
or: or
|
||||
deletionSuccess: Successfully deleted the dashboard
|
||||
deletionError: Error while deleting the dashboard
|
||||
search: Search
|
||||
selectPeriod: Select period
|
||||
all: All
|
||||
tableHead1: Dashboard name
|
||||
tableHead2: Agent name
|
||||
tableHead3: Dashboard type
|
||||
tableHead4: Data source type
|
||||
tableHead5: Last viewed
|
||||
createDashboard: Create dashboard
|
||||
title: Application Dashboard
|
||||
view: View
|
||||
configure: Configure
|
||||
delete: Delete
|
||||
noRecords: No dashboard available
|
||||
loading: Loading available dashboards
|
||||
error: Unable to fetch data
|
||||
modal:
|
||||
removeDashboard: Remove dashboard ?
|
||||
removeDashboardConfirmation: Are you sure you want to remove the dashboard
|
||||
cancel: Cancel
|
||||
delete: Delete
|
||||
applicationDashboards:
|
||||
createHeader: Create a new dashboard
|
||||
createHeader: Create dashboard
|
||||
configureHeader: Configure dashboard
|
||||
chooseADashboardType:
|
||||
header: Select a dashboard type
|
||||
|
@ -288,7 +316,11 @@ analyticsDashboard:
|
|||
form:
|
||||
name: Name
|
||||
agent: Agent
|
||||
noActiveAgent: No active agent found
|
||||
agentInactive: Agent is inactive
|
||||
dataSource: Data source
|
||||
noActiveDataSource: No active data source found
|
||||
dataSourceInactive: Data source is inactive
|
||||
dashboardType: Dashboard type
|
||||
applications: Applications
|
||||
selectNamespaces: Select namespaces
|
||||
|
@ -333,6 +365,14 @@ analyticsDashboard:
|
|||
lineGraph: Line graph
|
||||
resolution: Resolution
|
||||
resolutionInfo: Resolution
|
||||
removeMetric: Remove metric ?
|
||||
removeMetricConfirmation: Are you sure you want to remove the metric panel
|
||||
under: under
|
||||
cancel: Cancel
|
||||
delete: Delete
|
||||
discardChangesConfirmation: Are you sure to discard changes for the metric panel
|
||||
discardChangesInfo: The selected metric panel will be reverted back to the previous version
|
||||
yesProceed: Yes, Proceed
|
||||
stepper:
|
||||
steps:
|
||||
step1: Choose a dashboard type
|
||||
|
@ -367,14 +407,8 @@ analyticsDashboard:
|
|||
of2: of 2
|
||||
saveChanges: Save changes
|
||||
next: Next
|
||||
modalHeadingSuccessAdd: A new data source is successfully connected
|
||||
modalHeadingSuccessConfigure: Data source information is successfully updated
|
||||
modalHeadingFailed: There was a problem with connecting your data source
|
||||
modalBodySuccessAdd: You will see the data source connected in the data source table.
|
||||
modalBodySuccessConfigure: You will see the data source information updated in the data source table.
|
||||
modalBodyFailed: Try back again or check your entered details.
|
||||
modalActionsBack: Back to Data Source
|
||||
modalActionsTryAgain: Try Again
|
||||
adding: Adding
|
||||
updating: Updating
|
||||
general: General
|
||||
generalInfo: Provide a name and choose the data source type
|
||||
name: Name
|
||||
|
@ -398,22 +432,39 @@ analyticsDashboard:
|
|||
scrapeInterval: Scrape Interval
|
||||
queryTimeOut: Query timeout
|
||||
httpMethod: HTTP Method
|
||||
connectionSuccess: Successfully connected to the data source
|
||||
connectionError: Error connecting to the data source
|
||||
updateSuccess: Successfully updated the data source information
|
||||
updateError: Error updating the data source information
|
||||
dataSourceTable:
|
||||
search: Search
|
||||
selectPeriod: Select period
|
||||
all: All
|
||||
warning:
|
||||
text: Unexpected bad things will happen if you don’t read this!
|
||||
info: The data source seems to be connected to following Application dashboards. Dashboards will be unable to show metrics if the data source is deleted.
|
||||
connectedDashboards: "Connected Dashboards :"
|
||||
showLess: Show Less Dashboards
|
||||
dashboards: Dashboards
|
||||
deletionSuccess: Successfully deleted the data source
|
||||
deletionError: Error while deleting the data source
|
||||
tableHead1: Status
|
||||
tableHead2: Data Source Name
|
||||
tableHead3: Data Source Type
|
||||
tableHead4: Last Configured
|
||||
addDataSource: Add a Data Source
|
||||
tableHead2: Data source name
|
||||
tableHead3: Data source type
|
||||
tableHead4: Last configured
|
||||
tableHead5: Data source link
|
||||
addDataSource: Add data source
|
||||
loading: Loading available data sources
|
||||
noRecords: No data source available
|
||||
fetchError: Unable to fetch data
|
||||
dashboardTable:
|
||||
tableHead1: Dashboard Name
|
||||
tableHead2: Agent Name
|
||||
tableHead3: Dashboard Type
|
||||
tableHead4: Data Source Type
|
||||
tableHead5: Last viewed
|
||||
addDashboard: Add a Dashboard
|
||||
configure: Configure
|
||||
delete: Delete
|
||||
modal:
|
||||
removeDataSource: Remove data source ?
|
||||
removeDataSourceConfirmation: Are you sure you want to remove the data source
|
||||
cancel: Cancel
|
||||
delete: Delete
|
||||
forceDelete: Force Delete
|
||||
glowCard:
|
||||
title-0: Set it up
|
||||
heading-0: Add a Data Source
|
||||
|
@ -429,10 +480,10 @@ analyticsDashboard:
|
|||
configureBanner:
|
||||
case-0:
|
||||
heading: No data source is configured yet for any agent
|
||||
description: To configure your first Kubernetes dashboard you need to connect a data source. Select "Add a data source" to connect.
|
||||
description: To configure your first application dashboard you need to connect a data source. Select "Add a data source" to connect.
|
||||
case-1:
|
||||
heading: Data sources has been added, now configure a dashboard
|
||||
description: To configure your first Kubernets dashboard use the connect Data sources of this project
|
||||
heading: Data source(s) has been added, now configure a dashboard
|
||||
description: To configure your first application dashboard use the connect data sources of this project.
|
||||
timeText:
|
||||
lastRun: Last run
|
||||
lastOpened: Last opened
|
||||
|
@ -496,8 +547,8 @@ homeViews:
|
|||
recentWorkflowRuns:
|
||||
workflowRunCard:
|
||||
cardTitle: Browse workflow
|
||||
resilienceRate: 'Overall resilience rate:'
|
||||
lastRun: 'Last Run:'
|
||||
resilienceRate: "Overall resilience rate:"
|
||||
lastRun: "Last Run:"
|
||||
showAnalytics: Show the analytics
|
||||
downloadManifest: Download manifest
|
||||
heading: Recent Workflow runs
|
||||
|
@ -540,7 +591,7 @@ chaosWorkflows:
|
|||
disableSchedule: Disable Schedule
|
||||
enableSchedule: Enable Schedule
|
||||
saveTemplate: Save Template
|
||||
updateEngine: 'Note: Make sure to update the chaos engine names'
|
||||
updateEngine: "Note: Make sure to update the chaos engine names"
|
||||
saveChanges: Save Changes
|
||||
savedSuccessfully: Successfully saved template.
|
||||
cancel: Cancel
|
||||
|
@ -562,24 +613,13 @@ chaosWorkflows:
|
|||
sync: Sync Workflow
|
||||
terminate: Terminate Workflow
|
||||
tableData:
|
||||
overallRR: 'Overall RR : '
|
||||
experimentsPassed: 'Experiments Passed : '
|
||||
overallRR: "Overall RR : "
|
||||
experimentsPassed: "Experiments Passed : "
|
||||
showExperiments: Show Experiments
|
||||
showTheWorkflow: Show the workflow
|
||||
showTheAnalytics: Show the analytics
|
||||
na: NA
|
||||
|
||||
analyticsDashboardViews:
|
||||
kubernetesDashboard:
|
||||
form:
|
||||
dashboardPanels: Dashboard Panels
|
||||
table:
|
||||
seeAnalytics: See Analytics
|
||||
configure: Configure
|
||||
noRecords: No dashboard available
|
||||
loading: Loading available dashboards
|
||||
error: Unable to fetch data
|
||||
|
||||
settings:
|
||||
accountsTab:
|
||||
personalDetails:
|
||||
|
@ -763,7 +803,7 @@ settings:
|
|||
disconnect: Are you sure you want to disconnect?
|
||||
save: Save Locally
|
||||
repo: Git Repository
|
||||
desc: '* Any existing or active workflows saved locally will not be synced into the git repository'
|
||||
desc: "* Any existing or active workflows saved locally will not be synced into the git repository"
|
||||
connect: Connect
|
||||
branch: Branch
|
||||
URL: Git URL
|
||||
|
@ -778,11 +818,11 @@ settings:
|
|||
headingDesc: Your image registry for the workflow manifest
|
||||
defaultValues: Use the default values
|
||||
defaultText: All YAML files use these default values provided by Litmus. The values cannot be changed.
|
||||
registry: 'Registry : '
|
||||
repo: 'Repository : '
|
||||
repoType: 'Registry Type : '
|
||||
dockerio: 'docker.io'
|
||||
litmus: 'litmuschaos'
|
||||
registry: "Registry : "
|
||||
repo: "Repository : "
|
||||
repoType: "Registry Type : "
|
||||
dockerio: "docker.io"
|
||||
litmus: "litmuschaos"
|
||||
public: Public
|
||||
private: Private
|
||||
defaultReg: Use Default Registry
|
||||
|
@ -814,12 +854,12 @@ workflowDetailsView:
|
|||
Completed: Completed
|
||||
runTime:
|
||||
runTimeHeader: Run Time
|
||||
startTime: 'Start Time :'
|
||||
endTime: 'End Time :'
|
||||
startTime: "Start Time :"
|
||||
endTime: "End Time :"
|
||||
targets:
|
||||
targetsHeader: Target
|
||||
cluster: 'Cluster :'
|
||||
namespace: 'Workflow Namespace :'
|
||||
cluster: "Cluster :"
|
||||
namespace: "Workflow Namespace :"
|
||||
workflowNodeInfo:
|
||||
name: Name
|
||||
type: Type
|
||||
|
@ -874,8 +914,8 @@ createWorkflow:
|
|||
revertSchedule: Revert Schedule
|
||||
noTemplates: No template available.
|
||||
addTemplate: You can add a template from the scheduled workflows table.
|
||||
trueValue: 'True'
|
||||
falseValue: 'False'
|
||||
trueValue: "True"
|
||||
falseValue: "False"
|
||||
table:
|
||||
head1: Sequence
|
||||
head2: Name
|
||||
|
@ -970,8 +1010,8 @@ createWorkflow:
|
|||
myHubInfo: Experiment details
|
||||
annotationInfo: Provide target application details where you want to induce the chaos.
|
||||
annotation: Annotation Check
|
||||
true: 'True'
|
||||
false: 'False'
|
||||
true: "True"
|
||||
false: "False"
|
||||
annotationDesc: The target application does not have an annotation “Chaos=true”. Ideally, you cannot run this experiment unless the annotation is patched to the application. However, this service account has the privileges to disable the enforcement of annotation checks. Mark the annotation as false to proceed.
|
||||
appkind: appKind
|
||||
deployment: Deployment
|
||||
|
@ -980,7 +1020,7 @@ createWorkflow:
|
|||
deploymentconfig: Deploymentconfig
|
||||
rollout: Rollout
|
||||
nodeselector: NodeSelector
|
||||
selector: 'kubernetes.io/hostname :'
|
||||
selector: "kubernetes.io/hostname :"
|
||||
nsError: Namespace not available
|
||||
labelError: Label not available in the resource or namespace
|
||||
deleteExp: Delete the experiment
|
||||
|
@ -994,7 +1034,7 @@ createWorkflow:
|
|||
showDetails: Show Details
|
||||
showProp: Show Properties
|
||||
addProbe: Please add probes to see the data
|
||||
addNewProbe: '+ Add a new Probe'
|
||||
addNewProbe: "+ Add a new Probe"
|
||||
back: Back
|
||||
finish: Finish
|
||||
addProbe:
|
||||
|
@ -1176,7 +1216,7 @@ browseTemplate:
|
|||
#########################################
|
||||
|
||||
internetIssues:
|
||||
fetchData: 'Fetching the data...'
|
||||
fetchData: "Fetching the data..."
|
||||
connectionError: It seems you have no internet connection, Please try again When connectivity resumes.
|
||||
|
||||
######################################
|
||||
|
@ -1186,14 +1226,14 @@ myhub:
|
|||
title: MyHubs
|
||||
connectTarget: Connect the target
|
||||
viewMyHub: View My Hub
|
||||
error: '[Error: could not connect]'
|
||||
error: "[Error: could not connect]"
|
||||
view: View
|
||||
validationEmptySpace: Should not start with an empty space
|
||||
validURL: Enter a valid URL
|
||||
edit: Edit Hub
|
||||
refresh: Refresh Hub
|
||||
disconnect: Disconnect Hub
|
||||
lastSync: 'Last synced at:'
|
||||
lastSync: "Last synced at:"
|
||||
mainPage:
|
||||
header: ChaosHubs
|
||||
github: github.com/
|
||||
|
@ -1221,9 +1261,9 @@ myhub:
|
|||
chaosCharts: Chaos-charts
|
||||
noPredefinedExp: No predefined workflows available with information in this Hub
|
||||
noExp: No experiments found
|
||||
lastSynced: 'Last synced at: '
|
||||
repoLink: 'Repository Link: '
|
||||
repoBranch: 'Repository Branch: '
|
||||
lastSynced: "Last synced at: "
|
||||
repoLink: "Repository Link: "
|
||||
repoBranch: "Repository Branch: "
|
||||
connectHubPage:
|
||||
connectHub: Connect a new chaos hub
|
||||
editHub: Edit hub configuration
|
||||
|
@ -1241,7 +1281,7 @@ myhub:
|
|||
accessToken: Access Token
|
||||
ssh: SSH
|
||||
sshText: Please enable read / write access on the above git repository for the following key
|
||||
sshAlert: '*Make sure to provide the SSH link of the Git Repository'
|
||||
sshAlert: "*Make sure to provide the SSH link of the Git Repository"
|
||||
saveLater: Save for later
|
||||
updateHub: Update the MyHub with correct details from the MyHubs section
|
||||
cancel: Cancel
|
||||
|
|
|
@ -48,10 +48,6 @@ const StyledTab = withStyles((theme) =>
|
|||
wrapper: {
|
||||
flexDirection: 'row-reverse',
|
||||
},
|
||||
labelContainer: {
|
||||
width: 'auto',
|
||||
padding: 0,
|
||||
},
|
||||
})
|
||||
)((props: StyledTabProps) => <Tab {...props} />);
|
||||
|
||||
|
|
|
@ -237,6 +237,7 @@ const Routes: React.FC = () => {
|
|||
to="/analytics"
|
||||
/>
|
||||
<Redirect exact path="/analytics/datasource" to="/analytics" />
|
||||
<Redirect exact path="/analytics/dashboard" to="/analytics" />
|
||||
<Redirect exact path="/api-doc" to="/api-doc/index.html" />
|
||||
<Redirect to="/404" />
|
||||
</Switch>
|
||||
|
|
|
@ -14,6 +14,7 @@ const useStyles = makeStyles((theme: Theme) => ({
|
|||
|
||||
'& ::-webkit-scrollbar': {
|
||||
width: '0.4rem',
|
||||
height: '0.4rem',
|
||||
},
|
||||
'& ::-webkit-scrollbar-track': {
|
||||
marginTop: theme.spacing(1),
|
||||
|
|
|
@ -84,6 +84,7 @@ export interface DashboardDetails {
|
|||
export interface DashboardConfigurationDetails {
|
||||
name: string;
|
||||
typeID: string;
|
||||
typeName: string;
|
||||
dataSourceName: string;
|
||||
dataSourceURL: string;
|
||||
agentName: string;
|
||||
|
@ -97,10 +98,12 @@ export interface PanelNameAndID {
|
|||
export interface GraphPanelProps extends PanelResponse {
|
||||
className?: string;
|
||||
controllerPanelID?: string;
|
||||
selectedApplications?: string[];
|
||||
}
|
||||
|
||||
export interface GraphPanelGroupProps extends PanelGroupResponse {
|
||||
selectedPanels?: string[];
|
||||
selectedApplications?: string[];
|
||||
}
|
||||
|
||||
export interface ParsedPrometheusData {
|
||||
|
@ -108,34 +111,15 @@ export interface ParsedPrometheusData {
|
|||
closedAreaData: Array<GraphMetric>;
|
||||
chaosData: Array<GraphMetric>;
|
||||
}
|
||||
export interface RunWiseChaosMetrics {
|
||||
runIndex: number;
|
||||
runID: string;
|
||||
lastUpdatedTimeStamp: number;
|
||||
probeSuccessPercentage: string;
|
||||
experimentStatus: string;
|
||||
experimentVerdict: string;
|
||||
resilienceScore: string;
|
||||
workflowStatus: string;
|
||||
}
|
||||
|
||||
export interface WorkflowAndExperimentMetaDataMap {
|
||||
workflowID: string;
|
||||
workflowName: string;
|
||||
experimentName: string;
|
||||
targetApp: string;
|
||||
targetNamespace: string;
|
||||
runWiseChaosMetrics: RunWiseChaosMetrics[];
|
||||
}
|
||||
export interface ChaosEventDetails {
|
||||
id: string;
|
||||
legend: string;
|
||||
legendColor: string;
|
||||
chaosResultName: string;
|
||||
workflow: string;
|
||||
experiment: string;
|
||||
engineContext: string;
|
||||
target: string;
|
||||
result: string;
|
||||
chaosMetrics: WorkflowAndExperimentMetaDataMap;
|
||||
showOnTable: Boolean;
|
||||
verdict: string;
|
||||
}
|
||||
|
||||
export interface SelectedDashboardInformation {
|
||||
|
|
|
@ -28,6 +28,7 @@ import * as DashboardActions from '../../redux/actions/dashboards';
|
|||
import * as DataSourceActions from '../../redux/actions/dataSource';
|
||||
import { RootState } from '../../redux/reducers';
|
||||
import { getProjectID } from '../../utils/getSearchParams';
|
||||
import ChaosAccordion from '../../views/Analytics/ApplicationDashboard/ChaosAccordion';
|
||||
import DataSourceInactiveModal from '../../views/Analytics/ApplicationDashboard/DataSourceInactiveModal';
|
||||
import InfoDropdown from '../../views/Analytics/ApplicationDashboard/InfoDropdown';
|
||||
import DashboardPanelGroup from '../../views/Analytics/ApplicationDashboard/Panel/DashboardPanelGroup';
|
||||
|
@ -79,6 +80,9 @@ const DashboardPage: React.FC = () => {
|
|||
};
|
||||
const [isInfoOpen, setIsInfoOpen] = React.useState<Boolean>(false);
|
||||
const [selectedPanels, setSelectedPanels] = React.useState<string[]>([]);
|
||||
const [selectedApplications, setSelectedApplications] = React.useState<
|
||||
string[]
|
||||
>([]);
|
||||
|
||||
// Apollo query to get the dashboards data
|
||||
const { data: dashboards } = useQuery<DashboardList, ListDashboardVars>(
|
||||
|
@ -98,6 +102,8 @@ const DashboardPage: React.FC = () => {
|
|||
}
|
||||
);
|
||||
|
||||
const postEventSelectionRoutine = (selectedEvents: string[]) => {};
|
||||
|
||||
useEffect(() => {
|
||||
if (dashboards && dashboards.ListDashboard.length) {
|
||||
if (
|
||||
|
@ -291,18 +297,21 @@ const DashboardPage: React.FC = () => {
|
|||
dashboardConfigurationDetails={{
|
||||
name: selectedDashboardInformation.name,
|
||||
typeID: selectedDashboardInformation.typeID,
|
||||
typeName: selectedDashboardInformation.typeName,
|
||||
dataSourceName: selectedDataSource.selectedDataSourceName,
|
||||
dataSourceURL: selectedDataSource.selectedDataSourceURL,
|
||||
agentName: selectedDashboardInformation.agentName,
|
||||
}}
|
||||
metricsToBeShown={selectedDashboardInformation.panelNameAndIDList}
|
||||
applicationsToBeShown={[]}
|
||||
postPanelSelectionRoutine={(selectedPanelList: string[]) => {
|
||||
setSelectedPanels(selectedPanelList);
|
||||
}}
|
||||
applicationsToBeShown={
|
||||
selectedDashboardInformation.applicationMetadataMap
|
||||
}
|
||||
postPanelSelectionRoutine={(selectedPanelList: string[]) =>
|
||||
setSelectedPanels(selectedPanelList)
|
||||
}
|
||||
postApplicationSelectionRoutine={(
|
||||
selectedApplicationList: string[]
|
||||
) => {}}
|
||||
) => setSelectedApplications(selectedApplicationList)}
|
||||
/>
|
||||
)}
|
||||
<ToolBar />
|
||||
|
@ -310,13 +319,13 @@ const DashboardPage: React.FC = () => {
|
|||
className={classes.analyticsDiv}
|
||||
key={selectedDashboardInformation.dashboardKey}
|
||||
>
|
||||
{/* <div className={classes.chaosTableSection}>
|
||||
<div className={classes.chaosTableSection}>
|
||||
<ChaosAccordion
|
||||
dashboardKey={selectedDashboardInformation.dashboardKey}
|
||||
chaosEventsToBeShown={prometheusQueryData?.chaosEventsToBeShown}
|
||||
chaosEventsToBeShown={[]}
|
||||
postEventSelectionRoutine={postEventSelectionRoutine}
|
||||
/>
|
||||
</div> */}
|
||||
</div>
|
||||
{selectedDashboardInformation.metaData[0] &&
|
||||
selectedDashboardInformation.metaData[0].panel_groups.length >
|
||||
0 &&
|
||||
|
@ -332,6 +341,7 @@ const DashboardPage: React.FC = () => {
|
|||
panel_group_name={panelGroup.panel_group_name}
|
||||
panels={panelGroup.panels}
|
||||
selectedPanels={selectedPanels}
|
||||
selectedApplications={selectedApplications}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
|
|
|
@ -92,7 +92,13 @@ const ChooseAndConfigureDashboards: React.FC<ChooseAndConfigureDashboardsProps>
|
|||
const promQueries: PromQueryDetails[] = [];
|
||||
panel.prom_queries.forEach((promQuery) => {
|
||||
promQueries.push({
|
||||
...promQuery,
|
||||
queryid: promQuery.queryid,
|
||||
prom_query_name: promQuery.prom_query_name,
|
||||
legend: promQuery.legend,
|
||||
resolution: promQuery.resolution,
|
||||
minstep: promQuery.minstep,
|
||||
line: promQuery.line,
|
||||
close_area: promQuery.close_area,
|
||||
});
|
||||
});
|
||||
const panelOption: PanelOption = {
|
||||
|
@ -101,7 +107,11 @@ const ChooseAndConfigureDashboards: React.FC<ChooseAndConfigureDashboardsProps>
|
|||
left_axis: panel.panel_options.left_axis,
|
||||
};
|
||||
panels.push({
|
||||
...panel,
|
||||
panel_name: panel.panel_name,
|
||||
y_axis_left: panel.y_axis_left,
|
||||
y_axis_right: panel.y_axis_right,
|
||||
x_axis_down: panel.x_axis_down,
|
||||
unit: panel.unit,
|
||||
panel_options: panelOption,
|
||||
prom_queries: promQueries,
|
||||
panel_id: panel.panel_id,
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
import { useMutation } from '@apollo/client';
|
||||
import { Typography } from '@material-ui/core';
|
||||
import { ButtonFilled, ButtonOutlined, Modal } from 'litmus-ui';
|
||||
import { Snackbar, Typography } from '@material-ui/core';
|
||||
import { Alert } from '@material-ui/lab';
|
||||
import { ButtonFilled, ButtonOutlined } from 'litmus-ui';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useSelector } from 'react-redux';
|
||||
import BackButton from '../../components/Button/BackButton';
|
||||
import Loader from '../../components/Loader';
|
||||
import Scaffold from '../../containers/layouts/Scaffold';
|
||||
import { CREATE_DATASOURCE, UPDATE_DATASOURCE } from '../../graphql/mutations';
|
||||
import { DataSourceDetails } from '../../models/dataSourceData';
|
||||
|
@ -14,9 +16,12 @@ import {
|
|||
} from '../../models/graphql/dataSourceDetails';
|
||||
import { history } from '../../redux/configureStore';
|
||||
import { RootState } from '../../redux/reducers';
|
||||
import { ReactComponent as CrossMarkIcon } from '../../svg/crossmark.svg';
|
||||
import { getProjectID, getProjectRole } from '../../utils/getSearchParams';
|
||||
import { isValidWebUrl, validateTimeInSeconds } from '../../utils/validate';
|
||||
import {
|
||||
isValidWebUrl,
|
||||
validateTextEmpty,
|
||||
validateTimeInSeconds,
|
||||
} from '../../utils/validate';
|
||||
import ConfigurePrometheus from '../../views/Analytics/DataSources/Forms/prometheus';
|
||||
import useStyles from './styles';
|
||||
|
||||
|
@ -37,7 +42,6 @@ const DataSourceConfigurePage: React.FC<DataSourceConfigurePageProps> = ({
|
|||
);
|
||||
const projectID = getProjectID();
|
||||
const projectRole = getProjectRole();
|
||||
const [open, setOpen] = React.useState(false);
|
||||
const [disabled, setDisabled] = React.useState(true);
|
||||
const [page, setPage] = React.useState<number>(1);
|
||||
const [dataSourceVars, setDataSourceVars] = useState<DataSourceDetails>({
|
||||
|
@ -58,19 +62,21 @@ const DataSourceConfigurePage: React.FC<DataSourceConfigurePageProps> = ({
|
|||
httpMethod: 'POST',
|
||||
});
|
||||
const [mutate, setMutate] = React.useState(false);
|
||||
const [isAlertOpen, setIsAlertOpen] = React.useState(false);
|
||||
const [success, setSuccess] = React.useState(false);
|
||||
const [createDataSource] = useMutation<
|
||||
ListDataSourceResponse,
|
||||
CreateDataSourceInput
|
||||
>(CREATE_DATASOURCE, {
|
||||
onCompleted: () => {
|
||||
setSuccess(true);
|
||||
setMutate(false);
|
||||
setOpen(true);
|
||||
setSuccess(true);
|
||||
setIsAlertOpen(true);
|
||||
},
|
||||
onError: () => {
|
||||
setMutate(false);
|
||||
setOpen(true);
|
||||
setSuccess(false);
|
||||
setIsAlertOpen(true);
|
||||
},
|
||||
});
|
||||
const [updateDataSource] = useMutation<
|
||||
|
@ -78,13 +84,14 @@ const DataSourceConfigurePage: React.FC<DataSourceConfigurePageProps> = ({
|
|||
CreateDataSourceInput
|
||||
>(UPDATE_DATASOURCE, {
|
||||
onCompleted: () => {
|
||||
setSuccess(true);
|
||||
setMutate(false);
|
||||
setOpen(true);
|
||||
setSuccess(true);
|
||||
setIsAlertOpen(true);
|
||||
},
|
||||
onError: () => {
|
||||
setMutate(false);
|
||||
setOpen(true);
|
||||
setSuccess(false);
|
||||
setIsAlertOpen(true);
|
||||
},
|
||||
});
|
||||
|
||||
|
@ -160,7 +167,10 @@ const DataSourceConfigurePage: React.FC<DataSourceConfigurePageProps> = ({
|
|||
dataSourceVars.name === '' ||
|
||||
!isValidWebUrl(dataSourceVars.url) ||
|
||||
!validateTimeInSeconds(dataSourceVars.queryTimeout) ||
|
||||
!validateTimeInSeconds(dataSourceVars.scrapeInterval)
|
||||
!validateTimeInSeconds(dataSourceVars.scrapeInterval) ||
|
||||
(dataSourceVars.basicAuth &&
|
||||
(validateTextEmpty(dataSourceVars.username) ||
|
||||
validateTextEmpty(dataSourceVars.password)))
|
||||
) {
|
||||
setDisabled(true);
|
||||
} else {
|
||||
|
@ -193,8 +203,8 @@ const DataSourceConfigurePage: React.FC<DataSourceConfigurePageProps> = ({
|
|||
<BackButton />
|
||||
</div>
|
||||
<Typography className={classes.heading}>
|
||||
{t('analyticsDashboard.dataSourceForm.headingConfigure')} /{' '}
|
||||
{selectedDataSourceName}
|
||||
{t('analyticsDashboard.dataSourceForm.headingConfigure')} /
|
||||
{` ${selectedDataSourceName}`}
|
||||
</Typography>
|
||||
{dataSourceID ? (
|
||||
<ConfigurePrometheus
|
||||
|
@ -211,7 +221,9 @@ const DataSourceConfigurePage: React.FC<DataSourceConfigurePageProps> = ({
|
|||
</div>
|
||||
)}
|
||||
|
||||
<div className={classes.buttons}>
|
||||
<div
|
||||
className={`${classes.buttons} ${page === 2 ? '' : classes.flexEnd}`}
|
||||
>
|
||||
{page === 2 && (
|
||||
<ButtonOutlined onClick={() => setPage(1)} disabled={false}>
|
||||
<Typography>
|
||||
|
@ -221,13 +233,16 @@ const DataSourceConfigurePage: React.FC<DataSourceConfigurePageProps> = ({
|
|||
)}
|
||||
<div className={classes.saveButton}>
|
||||
<Typography className={classes.stepText}>
|
||||
{t('analyticsDashboard.dataSourceForm.step')}{' '}
|
||||
<strong>{page}</strong>{' '}
|
||||
{t('analyticsDashboard.dataSourceForm.step')}
|
||||
<strong>{` ${page} `}</strong>
|
||||
{t('analyticsDashboard.dataSourceForm.of2')}
|
||||
</Typography>
|
||||
<ButtonFilled
|
||||
disabled={
|
||||
page === 1 && dataSourceVars.name === ''
|
||||
(page === 1 &&
|
||||
(dataSourceVars.name === '' ||
|
||||
!isValidWebUrl(dataSourceVars.url))) ||
|
||||
mutate
|
||||
? true
|
||||
: page === 2
|
||||
? disabled
|
||||
|
@ -237,124 +252,56 @@ const DataSourceConfigurePage: React.FC<DataSourceConfigurePageProps> = ({
|
|||
page === 2 && !disabled ? setMutate(true) : setPage(2)
|
||||
}
|
||||
>
|
||||
<Typography>
|
||||
<Typography className={classes.buttonText}>
|
||||
{page === 2
|
||||
? `${t('analyticsDashboard.dataSourceForm.saveChanges')}`
|
||||
: `${t('analyticsDashboard.dataSourceForm.next')}`}
|
||||
? mutate
|
||||
? !configure
|
||||
? t('analyticsDashboard.dataSourceForm.adding')
|
||||
: t('analyticsDashboard.dataSourceForm.updating')
|
||||
: t('analyticsDashboard.dataSourceForm.saveChanges')
|
||||
: t('analyticsDashboard.dataSourceForm.next')}
|
||||
</Typography>
|
||||
{mutate && <Loader size={20} />}
|
||||
</ButtonFilled>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<Modal
|
||||
open={open}
|
||||
onClose={() => setOpen(false)}
|
||||
width="60%"
|
||||
modalActions={
|
||||
<ButtonOutlined
|
||||
className={classes.closeButton}
|
||||
onClick={() => setOpen(false)}
|
||||
>
|
||||
✕
|
||||
</ButtonOutlined>
|
||||
}
|
||||
>
|
||||
<div className={classes.modal}>
|
||||
<Typography align="center">
|
||||
{success === true ? (
|
||||
<img
|
||||
src="/icons/finish.svg"
|
||||
alt="success"
|
||||
className={classes.icon}
|
||||
/>
|
||||
) : (
|
||||
<CrossMarkIcon className={classes.icon} />
|
||||
)}
|
||||
</Typography>
|
||||
|
||||
<Typography
|
||||
className={classes.modalHeading}
|
||||
align="center"
|
||||
variant="h3"
|
||||
>
|
||||
{success === true && configure === false
|
||||
? `${t(
|
||||
'analyticsDashboard.dataSourceForm.modalHeadingSuccessAdd'
|
||||
)}`
|
||||
: success === true && configure === true
|
||||
? `${t(
|
||||
'analyticsDashboard.dataSourceForm.modalHeadingSuccessConfigure'
|
||||
)}`
|
||||
: `${t('analyticsDashboard.dataSourceForm.modalHeadingFailed')}`}
|
||||
</Typography>
|
||||
|
||||
<Typography
|
||||
align="center"
|
||||
variant="body1"
|
||||
className={classes.modalBody}
|
||||
>
|
||||
{success === true && configure === false ? (
|
||||
<div>
|
||||
{t('analyticsDashboard.dataSourceForm.modalBodySuccessAdd')}
|
||||
</div>
|
||||
) : success === true && configure === true ? (
|
||||
<div>
|
||||
{t(
|
||||
'analyticsDashboard.dataSourceForm.modalBodySuccessConfigure'
|
||||
)}
|
||||
</div>
|
||||
) : (
|
||||
<div>
|
||||
{t('analyticsDashboard.dataSourceForm.modalBodyFailed')}
|
||||
</div>
|
||||
)}
|
||||
</Typography>
|
||||
|
||||
{success === true ? (
|
||||
<ButtonFilled
|
||||
variant="success"
|
||||
onClick={() => {
|
||||
{isAlertOpen && (
|
||||
<Snackbar
|
||||
open={isAlertOpen}
|
||||
autoHideDuration={6000}
|
||||
onClose={() => {
|
||||
setIsAlertOpen(false);
|
||||
if (success) {
|
||||
history.push({
|
||||
pathname: '/analytics',
|
||||
search: `?projectID=${projectID}&projectRole=${projectRole}`,
|
||||
});
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Alert
|
||||
onClose={() => {
|
||||
setIsAlertOpen(false);
|
||||
if (success) {
|
||||
history.push({
|
||||
pathname: '/analytics',
|
||||
search: `?projectID=${projectID}&projectRole=${projectRole}`,
|
||||
});
|
||||
}}
|
||||
>
|
||||
<div>
|
||||
{t('analyticsDashboard.dataSourceForm.modalActionsBack')}
|
||||
</div>
|
||||
</ButtonFilled>
|
||||
) : (
|
||||
<div className={classes.flexButtons}>
|
||||
<ButtonOutlined
|
||||
onClick={() => {
|
||||
setOpen(false);
|
||||
setMutate(true);
|
||||
}}
|
||||
disabled={false}
|
||||
>
|
||||
<div>
|
||||
{t('analyticsDashboard.dataSourceForm.modalActionsTryAgain')}
|
||||
</div>
|
||||
</ButtonOutlined>
|
||||
|
||||
<ButtonFilled
|
||||
variant="error"
|
||||
onClick={() => {
|
||||
history.push({
|
||||
pathname: '/analytics',
|
||||
search: `?projectID=${projectID}&projectRole=${projectRole}`,
|
||||
});
|
||||
}}
|
||||
>
|
||||
<div>
|
||||
{t('analyticsDashboard.dataSourceForm.modalActionsBack')}
|
||||
</div>
|
||||
</ButtonFilled>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</Modal>
|
||||
}
|
||||
}}
|
||||
severity={success ? 'success' : 'error'}
|
||||
>
|
||||
{!configure
|
||||
? success
|
||||
? t('analyticsDashboard.dataSourceForm.connectionSuccess')
|
||||
: t('analyticsDashboard.dataSourceForm.connectionError')
|
||||
: success
|
||||
? t('analyticsDashboard.dataSourceForm.updateSuccess')
|
||||
: t('analyticsDashboard.dataSourceForm.updateError')}
|
||||
</Alert>
|
||||
</Snackbar>
|
||||
)}
|
||||
</Scaffold>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -49,6 +49,10 @@ const useStyles = makeStyles((theme) => ({
|
|||
paddingBottom: theme.spacing(7.5),
|
||||
},
|
||||
|
||||
flexEnd: {
|
||||
justifyContent: 'flex-end',
|
||||
},
|
||||
|
||||
saveButton: {
|
||||
display: 'flex',
|
||||
},
|
||||
|
@ -65,6 +69,10 @@ const useStyles = makeStyles((theme) => ({
|
|||
flexDirection: 'row',
|
||||
justifyContent: 'space-evenly',
|
||||
},
|
||||
|
||||
buttonText: {
|
||||
paddingRight: theme.spacing(1),
|
||||
},
|
||||
}));
|
||||
|
||||
export default useStyles;
|
||||
|
|
|
@ -1,14 +1,6 @@
|
|||
<svg width="15" height="16" viewBox="0 0 15 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0)">
|
||||
<path d="M14.6073 1.97255C14.3315 1.67929 13.944 1.51172 13.539 1.51172C12.7326 1.51172 12.0797 2.16457 12.0797 2.97103C12.0797 3.1002 12.0972 3.22938 12.1321 3.35506L10.5506 4.47572C9.985 3.90317 9.05984 3.89619 8.48729 4.46176C8.08231 4.86324 7.94615 5.46023 8.14166 5.99788L6.68235 7.22677C6.22151 6.56694 5.31381 6.40285 4.65049 6.86369C4.39912 7.03824 4.2106 7.28961 4.10935 7.57588L2.89792 7.08363C2.76176 6.28764 2.01116 5.75349 1.21517 5.88965C0.419185 6.0258 -0.114964 6.77641 0.0211916 7.57239C0.157347 8.36838 0.907949 8.90253 1.70394 8.76638C2.22761 8.6791 2.66052 8.31252 2.83857 7.81328L4.05001 8.30554C4.18616 9.10153 4.93676 9.63568 5.73275 9.49952C6.43448 9.38082 6.94768 8.77336 6.94768 8.06116C6.94768 8.01577 6.94419 7.9669 6.9407 7.92151L8.53267 6.57741C9.12966 7.11854 10.0513 7.07665 10.5925 6.47966C10.9381 6.09912 11.0568 5.56497 10.9102 5.07621L12.4742 3.96601C13.0258 4.55602 13.9475 4.58395 14.5375 4.03584C15.1275 3.48423 15.1554 2.56256 14.6073 1.97255ZM1.45955 8.09258C1.04061 8.09258 0.698479 7.75044 0.698479 7.3315C0.698479 6.91256 1.04061 6.57043 1.45955 6.57043C1.8785 6.57043 2.22063 6.91256 2.22063 7.3315C2.22063 7.75044 1.8785 8.09258 1.45955 8.09258ZM5.48488 8.82573C5.06594 8.82573 4.7238 8.48359 4.7238 8.06465C4.7238 7.64571 5.06594 7.30357 5.48488 7.30357C5.90382 7.30357 6.24595 7.64571 6.24595 8.06465C6.24595 8.48359 5.90731 8.82573 5.48488 8.82573ZM9.51369 6.2632C9.09475 6.2632 8.75261 5.92107 8.75261 5.50213C8.75261 5.08319 9.09475 4.74105 9.51369 4.74105C9.93263 4.74105 10.2748 5.08319 10.2748 5.50213C10.2748 5.92107 9.93263 6.2632 9.51369 6.2632ZM13.539 3.7321C13.1201 3.7321 12.7779 3.38997 12.7779 2.97103C12.7779 2.55209 13.1201 2.20995 13.539 2.20995C13.958 2.20995 14.3001 2.55209 14.3001 2.97103C14.3001 3.39346 13.9614 3.7321 13.539 3.7321Z" fill="#1C0732"/>
|
||||
<path d="M14.5166 5.58948H12.5615C12.3695 5.58948 12.2124 5.74658 12.2124 5.93859V14.2476C12.2124 14.4396 12.3695 14.5967 12.5615 14.5967H14.5166C14.7086 14.5967 14.8657 14.4396 14.8657 14.2476V5.93859C14.8657 5.74658 14.7086 5.58948 14.5166 5.58948ZM14.1675 13.9334H12.9106V6.28771H14.1675V13.9334Z" fill="#1C0732"/>
|
||||
<path d="M10.5014 8.10315H8.54638C8.35437 8.10315 8.19727 8.26025 8.19727 8.45227V14.2476C8.19727 14.4396 8.35437 14.5967 8.54638 14.5967H10.5014C10.6935 14.5967 10.8506 14.4396 10.8506 14.2476V8.45227C10.8506 8.26025 10.6935 8.10315 10.5014 8.10315ZM10.1523 13.9334H8.8955V8.80138H10.1523V13.9334Z" fill="#1C0732"/>
|
||||
<path d="M6.45188 10.7214H4.49682C4.30481 10.7214 4.14771 10.8785 4.14771 11.0705V14.2824C4.14771 14.4744 4.30481 14.6315 4.49682 14.6315H6.45188C6.6439 14.6315 6.801 14.4744 6.801 14.2824V11.0705C6.801 10.8785 6.6439 10.7214 6.45188 10.7214ZM6.10277 13.9333H4.84594V11.4196H6.10277V13.9333Z" fill="#1C0732"/>
|
||||
<path d="M2.43711 9.98822H0.482052C0.290037 9.98822 0.132935 10.1453 0.132935 10.3373V14.2824C0.132935 14.4744 0.290037 14.6315 0.482052 14.6315H2.43711C2.62912 14.6315 2.78623 14.4744 2.78623 14.2824V10.3373C2.78623 10.1453 2.62912 9.98822 2.43711 9.98822ZM2.08799 13.9332H0.831169V10.6865H2.08799V13.9332Z" fill="#1C0732"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0">
|
||||
<rect width="15" height="15" fill="white" transform="translate(0 0.572266)"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
<svg width="17" height="16" viewBox="0 0 17 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M9.97229 9.33594H7.03111C6.78499 9.33594 6.58548 9.53545 6.58548 9.78157V15.5525C6.58548 15.7986 6.78499 15.9982 7.03111 15.9982H9.97229C10.2184 15.9982 10.4179 15.7986 10.4179 15.5525V9.78157C10.4179 9.53545 10.2184 9.33594 9.97229 9.33594ZM9.52665 15.1069H7.47675V10.2272H9.52665V15.1069Z" fill="#696F8C"/>
|
||||
<path d="M15.8101 5.83594H12.8466C12.6004 5.83594 12.4009 6.03545 12.4009 6.28157V15.5508C12.4009 15.7969 12.6004 15.9964 12.8466 15.9964H15.8101C16.0562 15.9964 16.2557 15.7969 16.2557 15.5508V6.28157C16.2557 6.03549 16.0562 5.83594 15.8101 5.83594ZM15.3644 15.1051H13.2922V6.72721H15.3644V15.1051Z" fill="#696F8C"/>
|
||||
<path d="M4.15725 11.5H1.19377C0.947648 11.5 0.748138 11.6995 0.748138 11.9456V15.5553C0.748138 15.8014 0.947648 16.0009 1.19377 16.0009H4.15725C4.40337 16.0009 4.60288 15.8014 4.60288 15.5553V11.9456C4.60288 11.6996 4.40337 11.5 4.15725 11.5ZM3.71161 15.1096H1.63941V12.3913H3.71161V15.1096Z" fill="#696F8C"/>
|
||||
<path d="M16.2995 0.446908C16.2317 0.353508 16.1251 0.296103 16.0098 0.290942L11.9545 0.00127327C11.7084 -0.0171936 11.4939 0.167389 11.4754 0.413471C11.457 0.659596 11.6415 0.874076 11.8876 0.892542L14.7578 1.08745L9.48123 5.21523L5.44826 2.05122C5.27604 1.91483 5.03017 1.9243 4.86892 2.07348L0.724546 6.03958C0.548365 6.20672 0.538471 6.48419 0.702284 6.66349C0.781781 6.76665 0.9064 6.82483 1.03652 6.81945C1.15406 6.81779 1.26618 6.76972 1.34845 6.68575L5.2032 2.98697L9.1916 6.12867C9.35439 6.25901 9.58585 6.25901 9.74864 6.12867L15.4528 1.6899L15.2299 4.45762C15.2288 4.69961 15.4124 4.90249 15.6533 4.92552H15.6755C15.9053 4.92667 16.0982 4.753 16.1212 4.52445L16.4108 0.758839C16.4272 0.642878 16.3856 0.526277 16.2995 0.446908Z" fill="#696F8C"/>
|
||||
</svg>
|
||||
|
|
Before Width: | Height: | Size: 3.3 KiB After Width: | Height: | Size: 1.8 KiB |
|
@ -0,0 +1,5 @@
|
|||
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M9 6.5V9.5C9 9.76522 8.89464 10.0196 8.70711 10.2071C8.51957 10.3946 8.26522 10.5 8 10.5H2.5C2.23478 10.5 1.98043 10.3946 1.79289 10.2071C1.60536 10.0196 1.5 9.76522 1.5 9.5V4C1.5 3.73478 1.60536 3.48043 1.79289 3.29289C1.98043 3.10536 2.23478 3 2.5 3H5.5" stroke="#5B44BA" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M7.5 1.5H10.5V4.5" stroke="#5B44BA" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M5 7L10.5 1.5" stroke="#5B44BA" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
After Width: | Height: | Size: 619 B |
After Width: | Height: | Size: 127 KiB |
|
@ -0,0 +1,14 @@
|
|||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0)">
|
||||
<path d="M17.4455 4.85906L12.7581 0.17168C12.6487 0.0622266 12.4994 0 12.3438 0H4.14062C3.17137 0 2.38281 0.788555 2.38281 1.75781V18.2422C2.38281 19.2114 3.17137 20 4.14062 20H15.8594C16.8286 20 17.6172 19.2114 17.6172 18.2422V5.27344C17.6172 5.11367 17.5501 4.96363 17.4455 4.85906ZM12.9297 2.00051L15.6167 4.6875H13.5156C13.1925 4.6875 12.9297 4.42465 12.9297 4.10156V2.00051ZM15.8594 18.8281H4.14062C3.81754 18.8281 3.55469 18.5653 3.55469 18.2422V1.75781C3.55469 1.43473 3.81754 1.17188 4.14062 1.17188H11.7578V4.10156C11.7578 5.07082 12.5464 5.85938 13.5156 5.85938H16.4453V18.2422C16.4453 18.5653 16.1825 18.8281 15.8594 18.8281Z" fill="#5B44BA"/>
|
||||
<path d="M13.5156 8.28125H6.48438C6.16078 8.28125 5.89844 8.54359 5.89844 8.86719C5.89844 9.19078 6.16078 9.45312 6.48438 9.45312H13.5156C13.8392 9.45312 14.1016 9.19078 14.1016 8.86719C14.1016 8.54359 13.8392 8.28125 13.5156 8.28125Z" fill="#5B44BA"/>
|
||||
<path d="M13.5156 10.625H6.48438C6.16078 10.625 5.89844 10.8873 5.89844 11.2109C5.89844 11.5345 6.16078 11.7969 6.48438 11.7969H13.5156C13.8392 11.7969 14.1016 11.5345 14.1016 11.2109C14.1016 10.8873 13.8392 10.625 13.5156 10.625Z" fill="#5B44BA"/>
|
||||
<path d="M13.5156 12.9688H6.48438C6.16078 12.9688 5.89844 13.2311 5.89844 13.5547C5.89844 13.8783 6.16078 14.1406 6.48438 14.1406H13.5156C13.8392 14.1406 14.1016 13.8783 14.1016 13.5547C14.1016 13.2311 13.8392 12.9688 13.5156 12.9688Z" fill="#5B44BA"/>
|
||||
<path d="M11.1719 15.3125H6.48438C6.16078 15.3125 5.89844 15.5748 5.89844 15.8984C5.89844 16.222 6.16078 16.4844 6.48438 16.4844H11.1719C11.4955 16.4844 11.7578 16.222 11.7578 15.8984C11.7578 15.5748 11.4955 15.3125 11.1719 15.3125Z" fill="#5B44BA"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0">
|
||||
<rect width="20" height="20" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
After Width: | Height: | Size: 1.8 KiB |
|
@ -0,0 +1,3 @@
|
|||
<svg width="15" height="15" viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M1.5 9.5H1V10V14V14.5H1.5H5.33219H5.83219V14V10V9.5H5.33219H1.5ZM8.80333 4.0541L8.47194 4.4L8.80333 4.7459L10.9711 7.00864L11.3322 7.3855L11.6932 7.00864L13.861 4.7459L14.1924 4.4L13.861 4.0541L11.6932 1.79136L11.3322 1.4145L10.9711 1.79136L8.80333 4.0541ZM3.41609 6.5C4.77045 6.5 5.83219 5.3603 5.83219 4C5.83219 2.6397 4.77045 1.5 3.41609 1.5C2.06174 1.5 1 2.6397 1 4C1 5.3603 2.06174 6.5 3.41609 6.5ZM11.0805 14.5C12.4348 14.5 13.4966 13.3603 13.4966 12C13.4966 10.6397 12.4348 9.5 11.0805 9.5C9.72612 9.5 8.66438 10.6397 8.66438 12C8.66438 13.3603 9.72612 14.5 11.0805 14.5Z" stroke="#696F8C"/>
|
||||
</svg>
|
After Width: | Height: | Size: 711 B |
|
@ -1,6 +1,3 @@
|
|||
<svg width="16" height="15" viewBox="0 0 16 15" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M5 2.5C5 3.88071 3.88071 5 2.5 5C1.11929 5 0 3.88071 0 2.5C0 1.11929 1.11929 0 2.5 0C3.88071 0 5 1.11929 5 2.5Z" fill="#1C0732"/>
|
||||
<path d="M15 12.5C15 13.8807 13.8807 15 12.5 15C11.1193 15 10 13.8807 10 12.5C10 11.1193 11.1193 10 12.5 10C13.8807 10 15 11.1193 15 12.5Z" fill="#1C0732"/>
|
||||
<path d="M0 10H5V15H0V10Z" fill="#1C0732"/>
|
||||
<path d="M10 3L12.8284 0.171573L15.6569 3L12.8284 5.82843L10 3Z" fill="#1C0732"/>
|
||||
<svg width="19" height="20" viewBox="0 0 19 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M1.5 12.1784H1V12.6784V18.0117V18.5117H1.5H6.60958H7.10958V18.0117V12.6784V12.1784H6.60958H1.5ZM11.3581 4.86582L11.0267 5.21172L11.3581 5.55762L14.2485 8.57461L14.6096 8.95146L14.9706 8.57461L17.861 5.55762L18.1924 5.21172L17.861 4.86582L14.9706 1.84883L14.6096 1.47198L14.2485 1.84883L11.3581 4.86582ZM4.05479 7.84505C5.76189 7.84505 7.10958 6.40688 7.10958 4.67839C7.10958 2.9499 5.76189 1.51172 4.05479 1.51172C2.3477 1.51172 1 2.9499 1 4.67839C1 6.40688 2.3477 7.84505 4.05479 7.84505ZM14.274 18.5117C15.9811 18.5117 17.3287 17.0735 17.3287 15.3451C17.3287 13.6166 15.9811 12.1784 14.274 12.1784C12.5669 12.1784 11.2192 13.6166 11.2192 15.3451C11.2192 17.0735 12.5669 18.5117 14.274 18.5117Z" stroke="#1C0732"/>
|
||||
</svg>
|
||||
|
|
Before Width: | Height: | Size: 525 B After Width: | Height: | Size: 828 B |
|
@ -7,6 +7,7 @@ import {
|
|||
QueryLabelValue,
|
||||
} from '../models/dashboardsData';
|
||||
import {
|
||||
metricsTimeStampValue,
|
||||
PrometheusResponse,
|
||||
promQueryInput,
|
||||
} from '../models/graphql/prometheus';
|
||||
|
@ -69,7 +70,8 @@ export const DataParserForPrometheus = (
|
|||
prometheusData: PrometheusResponse,
|
||||
lineGraph: string[],
|
||||
areaGraph: string[],
|
||||
closedAreaQueryIDs: string[]
|
||||
closedAreaQueryIDs: string[],
|
||||
selectedApplications?: string[]
|
||||
) => {
|
||||
const parsedPrometheusData: ParsedPrometheusData = {
|
||||
seriesData: [],
|
||||
|
@ -98,11 +100,28 @@ export const DataParserForPrometheus = (
|
|||
prometheusData.GetPromQuery.metricsResponse?.forEach(
|
||||
(queryResponse, mainIndex) => {
|
||||
if (queryResponse && queryResponse.legends && queryResponse.tsvs) {
|
||||
let { legends } = queryResponse;
|
||||
let { tsvs } = queryResponse;
|
||||
if (selectedApplications && selectedApplications.length) {
|
||||
const newLegends: string[] = [];
|
||||
const newTsvs: metricsTimeStampValue[][] = [];
|
||||
queryResponse.legends.forEach((legend, index) => {
|
||||
const filteredApps: string[] = selectedApplications.filter((app) =>
|
||||
legend.includes(app)
|
||||
);
|
||||
if (filteredApps.length) {
|
||||
newLegends.push(legend);
|
||||
newTsvs.push(queryResponse.tsvs[index]);
|
||||
}
|
||||
});
|
||||
legends = newLegends;
|
||||
tsvs = newTsvs;
|
||||
}
|
||||
if (closedAreaQueryIDs.includes(queryResponse.queryid)) {
|
||||
parsedPrometheusData.closedAreaData.push(
|
||||
...queryResponse.legends.map((elem, index) => ({
|
||||
...legends.map((elem, index) => ({
|
||||
metricName: elem,
|
||||
data: queryResponse.tsvs[index].map((dataPoint) => ({
|
||||
data: tsvs[index].map((dataPoint) => ({
|
||||
...dataPoint,
|
||||
})),
|
||||
baseColor:
|
||||
|
@ -113,9 +132,9 @@ export const DataParserForPrometheus = (
|
|||
);
|
||||
} else {
|
||||
parsedPrometheusData.seriesData.push(
|
||||
...queryResponse.legends.map((elem, index) => ({
|
||||
...legends.map((elem, index) => ({
|
||||
metricName: elem,
|
||||
data: queryResponse.tsvs[index].map((dataPoint) => ({
|
||||
data: tsvs[index].map((dataPoint) => ({
|
||||
...dataPoint,
|
||||
})),
|
||||
baseColor:
|
||||
|
|
|
@ -4,7 +4,7 @@ import AccordionDetails from '@material-ui/core/AccordionDetails';
|
|||
import MuiAccordionSummary from '@material-ui/core/AccordionSummary';
|
||||
import ArrowDropDownIcon from '@material-ui/icons/ArrowDropDown';
|
||||
import ArrowDropUpIcon from '@material-ui/icons/ArrowDropUp';
|
||||
import { ButtonFilled } from 'litmus-ui';
|
||||
import { TextButton } from 'litmus-ui';
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { ChaosEventDetails } from '../../../../models/dashboardsData';
|
||||
|
@ -78,11 +78,10 @@ const ChaosAccordion: React.FC<ChaosAccordionProps> = ({
|
|||
className={classes.accordionSummary}
|
||||
key={`chaos-table-${dashboardKey}`}
|
||||
>
|
||||
<ButtonFilled
|
||||
<TextButton
|
||||
className={classes.button}
|
||||
onClick={() => {
|
||||
setChaosTableOpen(!chaosTableOpen);
|
||||
}}
|
||||
onClick={() => setChaosTableOpen(!chaosTableOpen)}
|
||||
variant="highlight"
|
||||
startIcon={
|
||||
!chaosTableOpen ? (
|
||||
<ArrowDropDownIcon className={classes.tableDropIcon} />
|
||||
|
@ -93,14 +92,14 @@ const ChaosAccordion: React.FC<ChaosAccordionProps> = ({
|
|||
>
|
||||
<Typography className={classes.chaosHelperText}>
|
||||
{!chaosTableOpen
|
||||
? `${t(
|
||||
? t(
|
||||
'analyticsDashboard.monitoringDashboardPage.chaosTable.showTable'
|
||||
)}`
|
||||
: `${t(
|
||||
)
|
||||
: t(
|
||||
'analyticsDashboard.monitoringDashboardPage.chaosTable.hideTable'
|
||||
)}`}
|
||||
)}
|
||||
</Typography>
|
||||
</ButtonFilled>
|
||||
</TextButton>
|
||||
<IconButton
|
||||
aria-label="edit chaos query"
|
||||
aria-haspopup="true"
|
||||
|
@ -114,9 +113,9 @@ const ChaosAccordion: React.FC<ChaosAccordionProps> = ({
|
|||
<StyledAccordionDetails className={classes.accordionDetails}>
|
||||
<ChaosTable
|
||||
chaosList={chaosEventsToBeShown}
|
||||
selectEvents={(selectedEvents: string[]) => {
|
||||
postEventSelectionRoutine(selectedEvents);
|
||||
}}
|
||||
selectEvents={(selectedEvents: string[]) =>
|
||||
postEventSelectionRoutine(selectedEvents)
|
||||
}
|
||||
/>
|
||||
</StyledAccordionDetails>
|
||||
</Accordion>
|
||||
|
|
|
@ -4,7 +4,8 @@ const useStyles = makeStyles((theme) => ({
|
|||
accordionSummary: {
|
||||
display: 'flex',
|
||||
justifyItems: 'center',
|
||||
background: theme.palette.disabledBackground,
|
||||
background: theme.palette.cards.header,
|
||||
borderRadius: '3px 3px 0 0',
|
||||
},
|
||||
|
||||
accordionDetails: {
|
||||
|
|
|
@ -31,17 +31,21 @@ const TableData: React.FC<TableDataProps> = ({
|
|||
/>
|
||||
</StyledTableCell>
|
||||
<StyledTableCell>
|
||||
<div
|
||||
className={classes.colorBar}
|
||||
style={{
|
||||
background: data.legend,
|
||||
}}
|
||||
/>
|
||||
<Typography
|
||||
className={classes.tableObjects}
|
||||
style={{ maxWidth: '15rem' }}
|
||||
>
|
||||
<div
|
||||
className={classes.colorCircle}
|
||||
style={{ background: data.legendColor }}
|
||||
/>
|
||||
{data.chaosResultName}
|
||||
</Typography>
|
||||
</StyledTableCell>
|
||||
<StyledTableCell>
|
||||
<Typography
|
||||
className={classes.tableObjects}
|
||||
style={{ maxWidth: '25rem' }}
|
||||
style={{ maxWidth: '12.5rem' }}
|
||||
>
|
||||
{data.workflow}
|
||||
</Typography>
|
||||
|
@ -49,15 +53,15 @@ const TableData: React.FC<TableDataProps> = ({
|
|||
<StyledTableCell>
|
||||
<Typography
|
||||
className={classes.tableObjects}
|
||||
style={{ maxWidth: '10rem' }}
|
||||
style={{ maxWidth: '7.5rem' }}
|
||||
>
|
||||
{data.experiment}
|
||||
{data.engineContext}
|
||||
</Typography>
|
||||
</StyledTableCell>
|
||||
<StyledTableCell>
|
||||
<Typography
|
||||
className={classes.tableObjects}
|
||||
style={{ maxWidth: '15rem' }}
|
||||
style={{ maxWidth: '7.5rem' }}
|
||||
>
|
||||
{data.target}
|
||||
</Typography>
|
||||
|
@ -65,14 +69,14 @@ const TableData: React.FC<TableDataProps> = ({
|
|||
<StyledTableCell>
|
||||
<Typography
|
||||
className={`${classes.tableObjects} ${
|
||||
data.result === CHAOS_EXPERIMENT_VERDICT_PASS
|
||||
data.verdict === CHAOS_EXPERIMENT_VERDICT_PASS
|
||||
? classes.pass
|
||||
: data.result === CHAOS_EXPERIMENT_VERDICT_FAIL
|
||||
: data.verdict === CHAOS_EXPERIMENT_VERDICT_FAIL
|
||||
? classes.fail
|
||||
: classes.awaited
|
||||
}`}
|
||||
>
|
||||
{data.result}
|
||||
{data.verdict}
|
||||
</Typography>
|
||||
</StyledTableCell>
|
||||
</>
|
||||
|
|
|
@ -32,53 +32,42 @@ const TableHeader: React.FC<TableHeaderProps> = ({
|
|||
/>
|
||||
</StyledTableCell>
|
||||
<StyledTableCell className={classes.headSpacing}>
|
||||
<div className={classes.nameContent}>
|
||||
<div className={classes.nameHead}>
|
||||
{t(
|
||||
'analyticsDashboard.monitoringDashboardPage.chaosTable.tableHead1'
|
||||
)}
|
||||
</div>
|
||||
<div className={classes.nameHead}>
|
||||
{t(
|
||||
'analyticsDashboard.monitoringDashboardPage.chaosTable.tableHead1'
|
||||
)}
|
||||
</div>
|
||||
</StyledTableCell>
|
||||
<StyledTableCell className={classes.headSpacing}>
|
||||
<div className={classes.nameContent}>
|
||||
<div className={classes.nameHead}>
|
||||
{t(
|
||||
'analyticsDashboard.monitoringDashboardPage.chaosTable.tableHead2'
|
||||
)}
|
||||
</div>
|
||||
<div className={classes.nameHead}>
|
||||
{t(
|
||||
'analyticsDashboard.monitoringDashboardPage.chaosTable.tableHead2'
|
||||
)}
|
||||
</div>
|
||||
</StyledTableCell>
|
||||
<StyledTableCell className={classes.headSpacing}>
|
||||
<div className={classes.nameContent}>
|
||||
<div className={classes.nameHead}>
|
||||
{t(
|
||||
'analyticsDashboard.monitoringDashboardPage.chaosTable.tableHead3'
|
||||
)}
|
||||
</div>
|
||||
<div className={classes.nameHead}>
|
||||
{t(
|
||||
'analyticsDashboard.monitoringDashboardPage.chaosTable.tableHead3'
|
||||
)}
|
||||
</div>
|
||||
</StyledTableCell>
|
||||
<StyledTableCell className={classes.headSpacing}>
|
||||
<div className={classes.nameContent}>
|
||||
<div className={classes.nameHead}>
|
||||
{t(
|
||||
'analyticsDashboard.monitoringDashboardPage.chaosTable.tableHead4'
|
||||
)}
|
||||
</div>
|
||||
<div className={classes.nameHead}>
|
||||
{t(
|
||||
'analyticsDashboard.monitoringDashboardPage.chaosTable.tableHead4'
|
||||
)}
|
||||
</div>
|
||||
</StyledTableCell>
|
||||
<StyledTableCell className={classes.headSpacing}>
|
||||
<div className={classes.nameContent}>
|
||||
<div className={classes.nameHead}>
|
||||
{t(
|
||||
'analyticsDashboard.monitoringDashboardPage.chaosTable.tableHead5.title'
|
||||
)}
|
||||
</div>
|
||||
<div className={classes.nameHead}>
|
||||
{t(
|
||||
'analyticsDashboard.monitoringDashboardPage.chaosTable.tableHead5.title'
|
||||
)}
|
||||
<InfoTooltip
|
||||
value={t(
|
||||
'analyticsDashboard.monitoringDashboardPage.chaosTable.tableHead5.infoText'
|
||||
)}
|
||||
className={classes.infoIcon}
|
||||
/>
|
||||
</div>
|
||||
</StyledTableCell>
|
||||
|
|
|
@ -77,7 +77,11 @@ const ChaosTable: React.FC<ChaosTableProps> = ({ chaosList, selectEvents }) => {
|
|||
<div>
|
||||
<section className="table section">
|
||||
<Paper className={classes.tableBody}>
|
||||
<TableContainer className={classes.tableMain}>
|
||||
<TableContainer
|
||||
className={`${classes.tableMain} ${
|
||||
!chaosList.length ? classes.empty : ''
|
||||
}`}
|
||||
>
|
||||
<Table aria-label="simple table">
|
||||
<TableHeader
|
||||
onSelectAllClick={handleSelectAllClick}
|
||||
|
@ -120,9 +124,9 @@ const ChaosTable: React.FC<ChaosTableProps> = ({ chaosList, selectEvents }) => {
|
|||
<TableCell colSpan={6}>
|
||||
<div className={classes.noRecords}>
|
||||
<img
|
||||
src="/icons/cloudIcon.svg"
|
||||
src="/icons/dashboardUnavailable.svg"
|
||||
className={classes.cloudIcon}
|
||||
alt="Chaos cloud"
|
||||
alt="Chaos event unavailable"
|
||||
/>
|
||||
<Typography
|
||||
align="center"
|
||||
|
@ -153,6 +157,21 @@ const ChaosTable: React.FC<ChaosTableProps> = ({ chaosList, selectEvents }) => {
|
|||
page={page}
|
||||
onChangePage={handleChangePage}
|
||||
onChangeRowsPerPage={handleChangeRowsPerPage}
|
||||
SelectProps={{
|
||||
MenuProps: {
|
||||
anchorOrigin: {
|
||||
vertical: 'bottom',
|
||||
horizontal: 'right',
|
||||
},
|
||||
transformOrigin: {
|
||||
vertical: 'top',
|
||||
horizontal: 'right',
|
||||
},
|
||||
getContentAnchorEl: null,
|
||||
classes: { paper: classes.menuList },
|
||||
},
|
||||
}}
|
||||
classes={{ menuItem: classes.menuListItem }}
|
||||
/>
|
||||
)}
|
||||
</Paper>
|
||||
|
|
|
@ -6,10 +6,13 @@ const useStyles = makeStyles((theme) => ({
|
|||
width: '100%',
|
||||
flexDirection: 'column',
|
||||
overflow: 'hidden',
|
||||
boxShadow:
|
||||
'0 0.3px 0.9px rgba(0, 0, 0, 0.1), 0 1.6px 3.6px rgba(0, 0, 0, 0.13)',
|
||||
borderRadius: '3px 3px 0 0',
|
||||
},
|
||||
|
||||
tableMain: {
|
||||
background: theme.palette.cards.header,
|
||||
background: theme.palette.background.paper,
|
||||
maxHeight: '30rem',
|
||||
'&::-webkit-scrollbar': {
|
||||
width: '0.2em',
|
||||
|
@ -28,42 +31,47 @@ const useStyles = makeStyles((theme) => ({
|
|||
},
|
||||
},
|
||||
|
||||
empty: {
|
||||
'& td': {
|
||||
borderBottom: 0,
|
||||
},
|
||||
},
|
||||
|
||||
tableBody: {
|
||||
background: theme.palette.cards.header,
|
||||
background: theme.palette.background.paper,
|
||||
},
|
||||
|
||||
tableHead: {
|
||||
background: theme.palette.cards.header,
|
||||
background: theme.palette.background.paper,
|
||||
},
|
||||
|
||||
nameHead: {
|
||||
display: 'flex',
|
||||
color: theme.palette.text.hint,
|
||||
margin: theme.spacing(2, 0, 1.5),
|
||||
fontSize: '0.875rem',
|
||||
fontSize: '0.75rem',
|
||||
lineHeight: '150%',
|
||||
fontWeight: 500,
|
||||
letterSpacing: '0.02em',
|
||||
margin: theme.spacing(1, 0),
|
||||
},
|
||||
|
||||
tableObjects: {
|
||||
textAlign: 'center',
|
||||
paddingLeft: theme.spacing(1),
|
||||
display: 'flex',
|
||||
gap: '0.5rem',
|
||||
textAlign: 'left',
|
||||
color: theme.palette.text.primary,
|
||||
height: '1.75rem',
|
||||
marginTop: theme.spacing(2),
|
||||
fontSize: '0.875rem',
|
||||
lineHeight: '130%',
|
||||
fontSize: '0.75rem',
|
||||
letterSpacing: '0.02em',
|
||||
lineHeight: '150%',
|
||||
paddingLeft: theme.spacing(0.5),
|
||||
margin: theme.spacing(1, 0),
|
||||
overflow: 'hidden',
|
||||
textOverflow: 'ellipsis',
|
||||
whiteSpace: 'nowrap',
|
||||
},
|
||||
|
||||
headSpacing: {
|
||||
paddingLeft: theme.spacing(2.5),
|
||||
},
|
||||
|
||||
nameContent: {
|
||||
color: theme.palette.text.primary,
|
||||
display: 'flex',
|
||||
fontSize: '0.8rem',
|
||||
justifyContent: 'center',
|
||||
minWidth: '5rem',
|
||||
paddingLeft: theme.spacing(2),
|
||||
},
|
||||
|
||||
checkbox: {
|
||||
|
@ -103,14 +111,28 @@ const useStyles = makeStyles((theme) => ({
|
|||
marginRight: theme.spacing(1),
|
||||
},
|
||||
|
||||
colorBar: {
|
||||
height: '0.45rem',
|
||||
width: '2.75rem',
|
||||
margin: theme.spacing(0.75, 0, 0, 1.5),
|
||||
colorCircle: {
|
||||
height: '0.5rem',
|
||||
width: '0.5rem',
|
||||
borderRadius: '50%',
|
||||
marginTop: theme.spacing(0.75),
|
||||
},
|
||||
|
||||
infoIcon: {
|
||||
margin: theme.spacing(1.75, 0, 0, 0.5),
|
||||
// select
|
||||
menuList: {
|
||||
boxShadow: '0 5px 9px rgba(0, 0, 0, 0.1)',
|
||||
},
|
||||
menuListItem: {
|
||||
background: `${theme.palette.background.paper} !important`,
|
||||
fontSize: '0.875rem',
|
||||
lineHeight: '150%',
|
||||
height: '1.875rem',
|
||||
'&:hover': {
|
||||
background: `${theme.palette.cards.highlight} !important`,
|
||||
},
|
||||
'&.Mui-selected': {
|
||||
background: `${theme.palette.cards.highlight} !important`,
|
||||
},
|
||||
},
|
||||
}));
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
import { useMutation } from '@apollo/client';
|
||||
import { Snackbar, Typography } from '@material-ui/core';
|
||||
import { Alert } from '@material-ui/lab';
|
||||
import { ButtonFilled, ButtonOutlined, InputField, Modal } from 'litmus-ui';
|
||||
import { ButtonFilled, InputField, Modal, TextButton } from 'litmus-ui';
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
@ -160,24 +160,7 @@ const DashboardCloneModal: React.FC<DashboardCloneModalProps> = ({
|
|||
|
||||
return (
|
||||
<div>
|
||||
<Modal
|
||||
open
|
||||
onClose={() => {
|
||||
onClose();
|
||||
}}
|
||||
modalActions={
|
||||
<ButtonOutlined
|
||||
className={classes.closeButton}
|
||||
onClick={() => {
|
||||
onClose();
|
||||
}}
|
||||
>
|
||||
✕
|
||||
</ButtonOutlined>
|
||||
}
|
||||
width="45%"
|
||||
height="fit-content"
|
||||
>
|
||||
<Modal open onClose={() => onClose()} width="45%" height="fit-content">
|
||||
<div className={classes.modal}>
|
||||
<Typography className={classes.modalHeading} align="left">
|
||||
{t(
|
||||
|
@ -195,10 +178,8 @@ const DashboardCloneModal: React.FC<DashboardCloneModalProps> = ({
|
|||
value={cloneName}
|
||||
/>
|
||||
<div className={classes.flexButtons}>
|
||||
<ButtonOutlined
|
||||
onClick={() => {
|
||||
onClose();
|
||||
}}
|
||||
<TextButton
|
||||
onClick={() => onClose()}
|
||||
className={classes.cancelButton}
|
||||
>
|
||||
<Typography className={classes.buttonText}>
|
||||
|
@ -206,7 +187,7 @@ const DashboardCloneModal: React.FC<DashboardCloneModalProps> = ({
|
|||
'analyticsDashboard.monitoringDashboardPage.dashboardCloneModal.cancel'
|
||||
)}
|
||||
</Typography>
|
||||
</ButtonOutlined>
|
||||
</TextButton>
|
||||
<ButtonFilled onClick={() => handleCreateMutation()}>
|
||||
<Typography
|
||||
className={`${classes.buttonText} ${classes.okButtonText}`}
|
||||
|
|
|
@ -2,7 +2,7 @@ import { makeStyles } from '@material-ui/core';
|
|||
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
modalHeading: {
|
||||
margin: theme.spacing(6.5, 0, 7.5),
|
||||
margin: theme.spacing(2.5, 0, 4.5),
|
||||
paddingLeft: theme.spacing(6.5),
|
||||
fontSize: '1.5rem',
|
||||
},
|
||||
|
@ -17,13 +17,6 @@ const useStyles = makeStyles((theme) => ({
|
|||
padding: theme.spacing(5, 0),
|
||||
},
|
||||
|
||||
closeButton: {
|
||||
borderColor: theme.palette.border.main,
|
||||
color: theme.palette.border.main,
|
||||
padding: theme.spacing(0.5),
|
||||
minWidth: '2.5rem',
|
||||
},
|
||||
|
||||
buttonText: {
|
||||
lineHeight: '140%',
|
||||
fontSize: '0.875rem',
|
||||
|
@ -35,7 +28,6 @@ const useStyles = makeStyles((theme) => ({
|
|||
},
|
||||
|
||||
cancelButton: {
|
||||
borderColor: theme.palette.border.main,
|
||||
marginRight: theme.spacing(1.5),
|
||||
padding: theme.spacing(0, 3),
|
||||
},
|
||||
|
|
|
@ -30,19 +30,7 @@ const DataSourceInactiveModal: React.FC<DataSourceInactiveModalProps> = ({
|
|||
return (
|
||||
<Modal
|
||||
open
|
||||
onClose={() => {
|
||||
history.goBack();
|
||||
}}
|
||||
modalActions={
|
||||
<ButtonOutlined
|
||||
className={classes.closeButton}
|
||||
onClick={() => {
|
||||
history.goBack();
|
||||
}}
|
||||
>
|
||||
✕
|
||||
</ButtonOutlined>
|
||||
}
|
||||
onClose={() => history.goBack()}
|
||||
width="60%"
|
||||
height="fit-content"
|
||||
>
|
||||
|
@ -83,12 +71,12 @@ const DataSourceInactiveModal: React.FC<DataSourceInactiveModalProps> = ({
|
|||
{t('analyticsDashboard.monitoringDashboardPage.or')}
|
||||
</Typography>
|
||||
<ButtonOutlined
|
||||
onClick={() => {
|
||||
onClick={() =>
|
||||
history.push({
|
||||
pathname: '/analytics/datasource/configure',
|
||||
search: `?projectID=${projectID}&projectRole=${projectRole}`,
|
||||
});
|
||||
}}
|
||||
})
|
||||
}
|
||||
>
|
||||
<img
|
||||
src="/icons/disconnected.svg"
|
||||
|
|
|
@ -2,7 +2,7 @@ import { makeStyles } from '@material-ui/core';
|
|||
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
modalHeading: {
|
||||
margin: theme.spacing(3.5, 0, 2.5),
|
||||
margin: theme.spacing(2.5, 0),
|
||||
paddingLeft: theme.spacing(6.5),
|
||||
fontSize: '1.5rem',
|
||||
},
|
||||
|
@ -30,10 +30,6 @@ const useStyles = makeStyles((theme) => ({
|
|||
padding: theme.spacing(5, 0),
|
||||
},
|
||||
|
||||
closeButton: {
|
||||
borderColor: theme.palette.border.main,
|
||||
},
|
||||
|
||||
buttonIcon: {
|
||||
padding: theme.spacing(0, 1.5, 0, 0.5),
|
||||
},
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { FormControlLabel, FormGroup, Typography } from '@material-ui/core';
|
||||
import Icon from '@material-ui/core/Icon';
|
||||
import { FormControlLabel, Typography } from '@material-ui/core';
|
||||
import { TextButton } from 'litmus-ui';
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { CheckBox } from '../../../../components/CheckBox';
|
||||
|
@ -7,12 +7,18 @@ import {
|
|||
DashboardConfigurationDetails,
|
||||
PanelNameAndID,
|
||||
} from '../../../../models/dashboardsData';
|
||||
import useStyles from './styles';
|
||||
import {
|
||||
ApplicationMetadata,
|
||||
Resource,
|
||||
} from '../../../../models/graphql/dashboardsDetails';
|
||||
import { ReactComponent as ExternalLinkIcon } from '../../../../svg/externalLink.svg';
|
||||
import { ReactComponent as PrometheusIcon } from '../../../../svg/prometheus.svg';
|
||||
import useStyles, { FormGroupApplicationsGrid, FormGroupGrid } from './styles';
|
||||
|
||||
interface InfoDropdownProps {
|
||||
dashboardConfigurationDetails: DashboardConfigurationDetails;
|
||||
metricsToBeShown: PanelNameAndID[];
|
||||
applicationsToBeShown: string[];
|
||||
applicationsToBeShown: ApplicationMetadata[];
|
||||
postPanelSelectionRoutine: (selectedPanelList: string[]) => void;
|
||||
postApplicationSelectionRoutine: (selectedApplicationList: string[]) => void;
|
||||
}
|
||||
|
@ -29,7 +35,7 @@ const InfoDropdown: React.FC<InfoDropdownProps> = ({
|
|||
|
||||
const [selectedApplications, setSelectedApplications] = React.useState<
|
||||
string[]
|
||||
>(applicationsToBeShown);
|
||||
>([]);
|
||||
|
||||
const [selectedMetrics, setSelectedMetrics] = React.useState<string[]>(
|
||||
metricsToBeShown.map((metric) => metric.id)
|
||||
|
@ -88,7 +94,7 @@ const InfoDropdown: React.FC<InfoDropdownProps> = ({
|
|||
className={classes.inlineIcon}
|
||||
/>
|
||||
<Typography className={classes.infoValue}>
|
||||
{dashboardConfigurationDetails.typeID}
|
||||
{dashboardConfigurationDetails.typeName}
|
||||
</Typography>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -98,27 +104,19 @@ const InfoDropdown: React.FC<InfoDropdownProps> = ({
|
|||
'analyticsDashboard.monitoringDashboardPage.infoDropdown.metaData3'
|
||||
)}
|
||||
</Typography>
|
||||
<div className={classes.iconWithTextDiv}>
|
||||
<img
|
||||
src="/icons/prometheus.svg"
|
||||
alt="data source Icon"
|
||||
className={classes.inlineIcon}
|
||||
/>
|
||||
<TextButton
|
||||
className={classes.button}
|
||||
onClick={() =>
|
||||
window.open(dashboardConfigurationDetails.dataSourceURL)
|
||||
}
|
||||
startIcon={<PrometheusIcon className={classes.inlineIcon} />}
|
||||
endIcon={<ExternalLinkIcon className={classes.inlineIcon} />}
|
||||
classes={{ label: classes.buttonLabel }}
|
||||
>
|
||||
<Typography className={classes.infoValue}>
|
||||
{dashboardConfigurationDetails.dataSourceName}
|
||||
</Typography>
|
||||
<Icon
|
||||
onClick={() => {
|
||||
window.open(dashboardConfigurationDetails.dataSourceURL);
|
||||
}}
|
||||
>
|
||||
<img
|
||||
src="/icons/externalLink.svg"
|
||||
alt="external link"
|
||||
className={classes.linkIcon}
|
||||
/>
|
||||
</Icon>
|
||||
</div>
|
||||
</TextButton>
|
||||
</div>
|
||||
<div className={classes.dashboardMetaDataItem}>
|
||||
<Typography className={classes.infoKey}>
|
||||
|
@ -137,27 +135,47 @@ const InfoDropdown: React.FC<InfoDropdownProps> = ({
|
|||
'analyticsDashboard.monitoringDashboardPage.infoDropdown.subHeading2'
|
||||
)}
|
||||
</Typography>
|
||||
<div className={classes.checkBoxesContainer}>
|
||||
<FormGroup key="application-group">
|
||||
{applicationsToBeShown.map((application: string) => (
|
||||
<FormControlLabel
|
||||
control={
|
||||
<CheckBox
|
||||
checked={selectedApplications.includes(application)}
|
||||
onChange={() => handleApplicationSelect(application)}
|
||||
name={application}
|
||||
/>
|
||||
}
|
||||
label={
|
||||
<Typography className={classes.formControlLabel}>
|
||||
{application}
|
||||
<FormGroupApplicationsGrid key="application-group">
|
||||
{applicationsToBeShown?.map(
|
||||
(applicationMetadata: ApplicationMetadata) => (
|
||||
<div key={`${applicationMetadata.namespace}-namespace`}>
|
||||
<div className={classes.namespaceBox}>
|
||||
<Typography className={classes.infoKey}>
|
||||
{t(
|
||||
'analyticsDashboard.monitoringDashboardPage.infoDropdown.infoKeyNamespace'
|
||||
)}
|
||||
</Typography>
|
||||
}
|
||||
key={`${application}-application-label`}
|
||||
/>
|
||||
))}
|
||||
</FormGroup>
|
||||
</div>
|
||||
<Typography className={classes.infoValue}>
|
||||
{applicationMetadata.namespace}
|
||||
</Typography>
|
||||
</div>
|
||||
{applicationMetadata.applications.map(
|
||||
(resource: Resource) => (
|
||||
<div key={`${resource.kind}-resource`}>
|
||||
{resource.names.map((name: string) => (
|
||||
<FormControlLabel
|
||||
control={
|
||||
<CheckBox
|
||||
checked={selectedApplications.includes(name)}
|
||||
onChange={() => handleApplicationSelect(name)}
|
||||
name={name}
|
||||
/>
|
||||
}
|
||||
label={
|
||||
<Typography className={classes.formControlLabel}>
|
||||
{`${resource.kind} / ${name}`}
|
||||
</Typography>
|
||||
}
|
||||
key={`${resource.kind} / ${name}-application-label`}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
)}
|
||||
</FormGroupApplicationsGrid>
|
||||
</div>
|
||||
<div className={classes.infoSectionElement}>
|
||||
<Typography className={classes.sectionHeader}>
|
||||
|
@ -165,27 +183,25 @@ const InfoDropdown: React.FC<InfoDropdownProps> = ({
|
|||
'analyticsDashboard.monitoringDashboardPage.infoDropdown.subHeading3'
|
||||
)}
|
||||
</Typography>
|
||||
<div className={classes.checkBoxesContainer}>
|
||||
<FormGroup key="metric-group">
|
||||
{metricsToBeShown.map((metric: PanelNameAndID) => (
|
||||
<FormControlLabel
|
||||
control={
|
||||
<CheckBox
|
||||
checked={selectedMetrics.includes(metric.id)}
|
||||
onChange={() => handleMetricSelect(metric.id)}
|
||||
name={metric.name}
|
||||
/>
|
||||
}
|
||||
label={
|
||||
<Typography className={classes.formControlLabel}>
|
||||
{metric.name}
|
||||
</Typography>
|
||||
}
|
||||
key={`${metric}-metric-label`}
|
||||
/>
|
||||
))}
|
||||
</FormGroup>
|
||||
</div>
|
||||
<FormGroupGrid key="metric-group">
|
||||
{metricsToBeShown.map((metric: PanelNameAndID) => (
|
||||
<FormControlLabel
|
||||
control={
|
||||
<CheckBox
|
||||
checked={selectedMetrics.includes(metric.id)}
|
||||
onChange={() => handleMetricSelect(metric.id)}
|
||||
name={metric.name}
|
||||
/>
|
||||
}
|
||||
label={
|
||||
<Typography className={classes.formControlLabel}>
|
||||
{metric.name}
|
||||
</Typography>
|
||||
}
|
||||
key={`${metric}-metric-label`}
|
||||
/>
|
||||
))}
|
||||
</FormGroupGrid>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import { makeStyles } from '@material-ui/core';
|
||||
import { createStyles, FormGroup, makeStyles } from '@material-ui/core';
|
||||
import { withStyles } from '@material-ui/core/styles';
|
||||
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
root: {
|
||||
|
@ -33,6 +34,11 @@ const useStyles = makeStyles((theme) => ({
|
|||
padding: theme.spacing(1, 0, 3),
|
||||
letterSpacing: '0.1714px',
|
||||
},
|
||||
namespaceBox: {
|
||||
display: 'flex',
|
||||
gap: '1rem',
|
||||
margin: theme.spacing(0.5, 0, 1),
|
||||
},
|
||||
dashboardMetaDataItem: {
|
||||
display: 'grid',
|
||||
gridTemplateColumns: '1fr 1fr',
|
||||
|
@ -40,19 +46,13 @@ const useStyles = makeStyles((theme) => ({
|
|||
},
|
||||
infoKey: {
|
||||
fontSize: '0.825rem',
|
||||
lineHeight: '0.9375rem',
|
||||
lineHeight: '150%',
|
||||
color: theme.palette.highlight,
|
||||
},
|
||||
infoValue: {
|
||||
fontSize: '0.825rem',
|
||||
lineHeight: '150%',
|
||||
},
|
||||
checkBoxesContainer: {
|
||||
display: 'grid',
|
||||
gridTemplateColumns: '1fr 1fr',
|
||||
maxHeight: '7rem',
|
||||
overflowY: 'scroll',
|
||||
paddingLeft: theme.spacing(1),
|
||||
color: theme.palette.text.primary,
|
||||
},
|
||||
formControlLabel: {
|
||||
fontSize: '0.75rem',
|
||||
|
@ -60,18 +60,50 @@ const useStyles = makeStyles((theme) => ({
|
|||
lineHeight: '150%',
|
||||
},
|
||||
inlineIcon: {
|
||||
margin: theme.spacing(0.5, 1, 0, 0),
|
||||
width: '1rem',
|
||||
height: '1rem',
|
||||
},
|
||||
linkIcon: {
|
||||
margin: theme.spacing(0, 0, 0.45, 0.75),
|
||||
margin: theme.spacing(0.25, 0),
|
||||
width: '1rem',
|
||||
height: '1rem',
|
||||
},
|
||||
iconWithTextDiv: {
|
||||
display: 'flex',
|
||||
gap: '0.5rem',
|
||||
},
|
||||
button: {
|
||||
minWidth: 0,
|
||||
minHeight: 0,
|
||||
padding: 0,
|
||||
width: 'fit-content',
|
||||
'&:hover': {
|
||||
cursor: 'pointer !important',
|
||||
},
|
||||
},
|
||||
buttonLabel: {
|
||||
justifyContent: 'flex-start',
|
||||
marginLeft: theme.spacing(0.5),
|
||||
},
|
||||
}));
|
||||
|
||||
export const FormGroupGrid = withStyles((theme) =>
|
||||
createStyles({
|
||||
root: {
|
||||
display: 'grid',
|
||||
gridTemplateColumns: '1fr 1fr',
|
||||
maxHeight: '7rem',
|
||||
overflowY: 'auto',
|
||||
paddingLeft: theme.spacing(0.5),
|
||||
},
|
||||
})
|
||||
)(FormGroup);
|
||||
|
||||
export const FormGroupApplicationsGrid = withStyles((theme) =>
|
||||
createStyles({
|
||||
root: {
|
||||
display: 'grid',
|
||||
maxHeight: '7rem',
|
||||
overflowY: 'auto',
|
||||
paddingLeft: theme.spacing(0.5),
|
||||
},
|
||||
})
|
||||
)(FormGroup);
|
||||
|
||||
export default useStyles;
|
||||
|
|
|
@ -57,6 +57,7 @@ const PanelContent: React.FC<GraphPanelProps> = ({
|
|||
unit,
|
||||
className,
|
||||
controllerPanelID,
|
||||
selectedApplications,
|
||||
}) => {
|
||||
const { palette } = useTheme();
|
||||
const classes = useStyles();
|
||||
|
@ -120,7 +121,8 @@ const PanelContent: React.FC<GraphPanelProps> = ({
|
|||
areaGraph,
|
||||
prom_queries
|
||||
.filter((query) => query.close_area)
|
||||
.map((query) => query.queryid)
|
||||
.map((query) => query.queryid),
|
||||
selectedApplications
|
||||
);
|
||||
setGraphData(parsedData);
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@ interface DashboardPanelGroupContentProps {
|
|||
panel_group_name: string;
|
||||
panel_group_id: string;
|
||||
selectedPanels?: string[];
|
||||
selectedApplications?: string[];
|
||||
}
|
||||
|
||||
const DashboardPanelGroupContent: React.FC<DashboardPanelGroupContentProps> = ({
|
||||
|
@ -21,6 +22,7 @@ const DashboardPanelGroupContent: React.FC<DashboardPanelGroupContentProps> = ({
|
|||
panel_group_id,
|
||||
panel_group_name,
|
||||
selectedPanels,
|
||||
selectedApplications,
|
||||
}) => {
|
||||
const classes = useStyles();
|
||||
const [open, setOpen] = React.useState<boolean>(true);
|
||||
|
@ -63,6 +65,7 @@ const DashboardPanelGroupContent: React.FC<DashboardPanelGroupContentProps> = ({
|
|||
x_axis_down={panel.x_axis_down}
|
||||
unit={panel.unit}
|
||||
controllerPanelID={selectedPanels ? selectedPanels[0] : ''}
|
||||
selectedApplications={selectedApplications ?? []}
|
||||
/>
|
||||
))}
|
||||
</AccordionDetails>
|
||||
|
|
|
@ -22,8 +22,8 @@ const useStyles = makeStyles((theme) => ({
|
|||
alignContent: 'left',
|
||||
background: theme.palette.cards.header,
|
||||
boxShadow:
|
||||
'0px 0.3px 0.9px rgba(0, 0, 0, 0.1), 0px 1.6px 3.6px rgba(0, 0, 0, 0.13)',
|
||||
borderRadius: '3px 3px 0px 0px',
|
||||
'0 0.3px 0.9px rgba(0, 0, 0, 0.1), 0 1.6px 3.6px rgba(0, 0, 0, 0.13)',
|
||||
borderRadius: '3px 3px 0 0',
|
||||
},
|
||||
|
||||
panelGroupContainer: {
|
||||
|
@ -32,10 +32,10 @@ const useStyles = makeStyles((theme) => ({
|
|||
background: theme.palette.background.paper,
|
||||
display: 'grid',
|
||||
gridTemplateColumns: '49% 49%',
|
||||
gridGap: theme.spacing(1.75),
|
||||
gridGap: '0.875rem',
|
||||
padding: theme.spacing(1, 1, 1, 1.75),
|
||||
boxShadow:
|
||||
'0px 0.3px 0.9px rgba(0, 0, 0, 0.1), 0px 1.6px 3.6px rgba(0, 0, 0, 0.13)',
|
||||
'0 0.3px 0.9px rgba(0, 0, 0, 0.1), 0 1.6px 3.6px rgba(0, 0, 0, 0.13)',
|
||||
},
|
||||
|
||||
expand: {
|
||||
|
|
|
@ -127,7 +127,9 @@ const ToolBar: React.FC = () => {
|
|||
<div className={classes.controls}>
|
||||
<div ref={dateRangeSelectorRef}>
|
||||
<ButtonOutlined
|
||||
className={classes.selectDate}
|
||||
className={`${classes.selectDate} ${
|
||||
isDateRangeSelectorPopoverOpen ? classes.selectDateFocused : ''
|
||||
}`}
|
||||
onClick={() => setDateRangeSelectorPopoverOpen(true)}
|
||||
aria-label="time range"
|
||||
aria-haspopup="true"
|
||||
|
@ -249,6 +251,7 @@ const ToolBar: React.FC = () => {
|
|||
horizontal: 'right',
|
||||
},
|
||||
getContentAnchorEl: null,
|
||||
classes: { paper: classes.menuList },
|
||||
}}
|
||||
>
|
||||
<MenuItem
|
||||
|
@ -256,23 +259,7 @@ const ToolBar: React.FC = () => {
|
|||
value={MAX_REFRESH_RATE}
|
||||
className={classes.menuListItem}
|
||||
>
|
||||
<Typography
|
||||
className={classes.menuListItemText}
|
||||
style={{
|
||||
fontWeight: refreshRate === MAX_REFRESH_RATE ? 500 : 400,
|
||||
}}
|
||||
>
|
||||
{t('analyticsDashboard.monitoringDashboardPage.refresh.off')}
|
||||
</Typography>
|
||||
{refreshRate === MAX_REFRESH_RATE ? (
|
||||
<img
|
||||
src="/icons/check.svg"
|
||||
alt="check mark"
|
||||
className={classes.checkIcon}
|
||||
/>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
{t('analyticsDashboard.monitoringDashboardPage.refresh.off')}
|
||||
</MenuItem>
|
||||
{refreshData.map((data: RefreshObjectType) => (
|
||||
<MenuItem
|
||||
|
@ -280,23 +267,7 @@ const ToolBar: React.FC = () => {
|
|||
value={data.value}
|
||||
className={classes.menuListItem}
|
||||
>
|
||||
<Typography
|
||||
className={classes.menuListItemText}
|
||||
style={{
|
||||
fontWeight: refreshRate === data.value ? 500 : 400,
|
||||
}}
|
||||
>
|
||||
{t(data.label)}
|
||||
</Typography>
|
||||
{refreshRate === data.value ? (
|
||||
<img
|
||||
src="/icons/check.svg"
|
||||
alt="check mark"
|
||||
className={classes.checkIcon}
|
||||
/>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
{t(data.label)}
|
||||
</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
|
|
|
@ -29,6 +29,10 @@ const useStyles = makeStyles((theme) => ({
|
|||
},
|
||||
},
|
||||
|
||||
selectDateFocused: {
|
||||
border: `2px solid ${theme.palette.highlight}`,
|
||||
},
|
||||
|
||||
displayDate: {
|
||||
width: '100%',
|
||||
color: theme.palette.text.primary,
|
||||
|
@ -50,18 +54,21 @@ const useStyles = makeStyles((theme) => ({
|
|||
margin: theme.spacing(-2, 0, 0, -4.25),
|
||||
},
|
||||
|
||||
// select
|
||||
menuList: {
|
||||
boxShadow: '0 5px 9px rgba(0, 0, 0, 0.1)',
|
||||
},
|
||||
menuListItem: {
|
||||
background: `${theme.palette.background.paper} !important`,
|
||||
fontSize: '0.875rem',
|
||||
lineHeight: '150%',
|
||||
height: '1.75rem',
|
||||
width: '11.5rem',
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between',
|
||||
},
|
||||
|
||||
menuListItemText: {
|
||||
fontSize: '0.875rem',
|
||||
height: '1.875rem',
|
||||
'&:hover': {
|
||||
background: `${theme.palette.cards.highlight} !important`,
|
||||
},
|
||||
'&.Mui-selected': {
|
||||
background: `${theme.palette.cards.highlight} !important`,
|
||||
},
|
||||
},
|
||||
|
||||
checkIcon: {
|
||||
|
@ -132,10 +139,10 @@ export const useOutlinedInputStyles = makeStyles((theme: Theme) => ({
|
|||
borderColor: theme.palette.border.main,
|
||||
},
|
||||
'&:hover $notchedOutline': {
|
||||
borderColor: theme.palette.primary.main,
|
||||
borderColor: theme.palette.highlight,
|
||||
},
|
||||
'&$focused $notchedOutline': {
|
||||
borderColor: theme.palette.primary.main,
|
||||
borderColor: theme.palette.highlight,
|
||||
},
|
||||
'& .MuiInputLabel-root': {
|
||||
color: `${theme.palette.text.hint} !important`,
|
||||
|
|
|
@ -150,13 +150,14 @@ const TopNavButtons: React.FC<TopNavButtonsProps> = ({
|
|||
};
|
||||
|
||||
return (
|
||||
<div className={classes.button}>
|
||||
<div className={classes.buttons}>
|
||||
{navButtonStates.isInfoToggled ? (
|
||||
<ButtonFilled
|
||||
onClick={() => {
|
||||
setNavButtonStates({ ...navButtonStates, isInfoToggled: false });
|
||||
switchIsInfoToggled(false);
|
||||
}}
|
||||
className={classes.button}
|
||||
>
|
||||
<img
|
||||
src="/icons/infoWhite.svg"
|
||||
|
@ -173,6 +174,7 @@ const TopNavButtons: React.FC<TopNavButtonsProps> = ({
|
|||
setNavButtonStates({ ...navButtonStates, isInfoToggled: true });
|
||||
switchIsInfoToggled(true);
|
||||
}}
|
||||
className={classes.button}
|
||||
>
|
||||
<img src="/icons/info.svg" alt="Info icon" className={classes.icon} />
|
||||
<Typography className={classes.infoText}>
|
||||
|
@ -190,7 +192,7 @@ const TopNavButtons: React.FC<TopNavButtonsProps> = ({
|
|||
isOptionsToggled: false,
|
||||
});
|
||||
}}
|
||||
className={classes.optionsButton}
|
||||
className={classes.button}
|
||||
>
|
||||
<img
|
||||
src="/icons/menu-active.svg"
|
||||
|
@ -207,7 +209,7 @@ const TopNavButtons: React.FC<TopNavButtonsProps> = ({
|
|||
isOptionsToggled: true,
|
||||
});
|
||||
}}
|
||||
className={classes.optionsButton}
|
||||
className={classes.button}
|
||||
>
|
||||
<img
|
||||
src="/icons/menu.svg"
|
||||
|
@ -262,7 +264,7 @@ const TopNavButtons: React.FC<TopNavButtonsProps> = ({
|
|||
data-cy="optionsConfigureDashboard"
|
||||
className={classes.btnText}
|
||||
>
|
||||
{t('analyticsDashboardViews.kubernetesDashboard.table.configure')}
|
||||
{t('analyticsDashboard.applicationDashboardTable.configure')}
|
||||
</Typography>
|
||||
</div>
|
||||
</MenuItem>
|
||||
|
|
|
@ -5,10 +5,11 @@ const useStyles = makeStyles((theme) => ({
|
|||
margin: theme.spacing(0, 1),
|
||||
width: '1rem',
|
||||
},
|
||||
button: {
|
||||
buttons: {
|
||||
position: 'relative',
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between',
|
||||
gap: '1rem',
|
||||
},
|
||||
infoText: {
|
||||
paddingRight: theme.spacing(1.5),
|
||||
|
@ -17,17 +18,17 @@ const useStyles = makeStyles((theme) => ({
|
|||
margin: theme.spacing(0, 1),
|
||||
width: '1.25rem',
|
||||
},
|
||||
optionsButton: {
|
||||
marginLeft: theme.spacing(2),
|
||||
button: {
|
||||
minWidth: 0,
|
||||
padding: theme.spacing(1.5, 1),
|
||||
},
|
||||
|
||||
// Menu option with icon
|
||||
menuList: {
|
||||
marginLeft: theme.spacing(-1),
|
||||
marginLeft: theme.spacing(-3),
|
||||
},
|
||||
menuItem: {
|
||||
width: '11.5rem',
|
||||
width: '9.5rem',
|
||||
height: '2.5rem',
|
||||
},
|
||||
expDiv: {
|
||||
|
|
|
@ -82,15 +82,11 @@ const DashboardCards: React.FC<DashboardCardsProps> = ({
|
|||
{upload && (
|
||||
<Modal
|
||||
open
|
||||
onClose={() => {
|
||||
setUpload(false);
|
||||
}}
|
||||
onClose={() => setUpload(false)}
|
||||
modalActions={
|
||||
<ButtonOutlined
|
||||
className={classes.closeButton}
|
||||
onClick={() => {
|
||||
setUpload(false);
|
||||
}}
|
||||
onClick={() => setUpload(false)}
|
||||
>
|
||||
✕
|
||||
</ButtonOutlined>
|
||||
|
@ -106,9 +102,7 @@ const DashboardCards: React.FC<DashboardCardsProps> = ({
|
|||
</Typography>
|
||||
<UploadJSON
|
||||
successHandler={() => handleClick()}
|
||||
errorHandler={() => {
|
||||
generateAlert();
|
||||
}}
|
||||
errorHandler={() => generateAlert()}
|
||||
/>
|
||||
</div>
|
||||
</Modal>
|
||||
|
|
|
@ -23,6 +23,12 @@ const useStyles = makeStyles((theme) => ({
|
|||
'&:hover': {
|
||||
border: `1px solid ${theme.palette.primary.main}`,
|
||||
},
|
||||
[theme.breakpoints.down('md')]: {
|
||||
height: '9rem',
|
||||
},
|
||||
[theme.breakpoints.down('sm')]: {
|
||||
height: 'fit-content',
|
||||
},
|
||||
},
|
||||
|
||||
// CARD MEDIA
|
||||
|
@ -40,6 +46,7 @@ const useStyles = makeStyles((theme) => ({
|
|||
|
||||
// CARD CONTENT
|
||||
cardContent: {
|
||||
padding: theme.spacing(1.25, 1, 0),
|
||||
display: 'grid',
|
||||
gridTemplateColumns: '0.2fr 0.8fr',
|
||||
},
|
||||
|
@ -78,8 +85,8 @@ const useStyles = makeStyles((theme) => ({
|
|||
closeButton: {
|
||||
borderColor: theme.palette.border.main,
|
||||
color: theme.palette.border.main,
|
||||
padding: theme.spacing(0.5),
|
||||
minWidth: '2.5rem',
|
||||
padding: theme.spacing(0.25, 2),
|
||||
minWidth: 0,
|
||||
},
|
||||
}));
|
||||
|
||||
|
|
|
@ -66,14 +66,69 @@ const DashboardMetadataForm: React.FC<DashboardMetadataFormProps> = ({
|
|||
const selectedDashboard = useSelector(
|
||||
(state: RootState) => state.selectDashboard
|
||||
);
|
||||
const [update, setUpdate] = useState(false);
|
||||
const [availableApplicationMetadataMap, setAvailableApplicationMetadataMap] =
|
||||
useState<ApplicationMetadata[]>([]);
|
||||
const [kubeObjInput, setKubeObjInput] = useState<GVRRequest>({
|
||||
group: '',
|
||||
version: 'v1',
|
||||
resource: 'pods',
|
||||
});
|
||||
const [selectedNamespaceList, setSelectedNamespaceList] = useState<
|
||||
Array<Option>
|
||||
>([]);
|
||||
const [activeAgents, setActiveAgents] = useState<Cluster[]>([]);
|
||||
|
||||
const getSelectedApps = (dashboardJSON: any) => {
|
||||
dashboard.selectDashboard({
|
||||
selectedDashboardID: '',
|
||||
});
|
||||
return dashboardJSON.applicationMetadataMap;
|
||||
const selectedApps: ApplicationMetadata[] = [];
|
||||
dashboardJSON.applicationMetadataMap?.forEach(
|
||||
(applicationMetadata: ApplicationMetadata) => {
|
||||
const namespaceApps = availableApplicationMetadataMap.filter(
|
||||
(appMeta) => appMeta.namespace === applicationMetadata.namespace
|
||||
)[0];
|
||||
applicationMetadata.applications.forEach((app) => {
|
||||
const kindApps = namespaceApps.applications.filter(
|
||||
(appKind) => appKind.kind === app.kind
|
||||
)[0];
|
||||
const availableApps = app.names.filter((name) =>
|
||||
kindApps.names.includes(name)
|
||||
);
|
||||
if (availableApps.length) {
|
||||
let nsIndex = -1;
|
||||
selectedApps.forEach((existingApp, index) => {
|
||||
if (existingApp.namespace === applicationMetadata.namespace) {
|
||||
nsIndex = index;
|
||||
}
|
||||
});
|
||||
if (nsIndex !== -1) {
|
||||
selectedApps[nsIndex].applications.push({
|
||||
kind: app.kind,
|
||||
names: availableApps,
|
||||
});
|
||||
} else {
|
||||
selectedApps.push({
|
||||
namespace: applicationMetadata.namespace,
|
||||
applications: [
|
||||
{
|
||||
kind: app.kind,
|
||||
names: availableApps,
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
);
|
||||
return selectedApps;
|
||||
};
|
||||
|
||||
const [activeDataSources, setActiveDataSources] = useState<
|
||||
ListDataSourceResponse[]
|
||||
>([]);
|
||||
const [dashboardDetails, setDashboardDetails] = useState<DashboardDetails>({
|
||||
id: !configure ? '' : dashboardVars.id ?? '',
|
||||
name: !configure
|
||||
|
@ -114,29 +169,20 @@ const DashboardMetadataForm: React.FC<DashboardMetadataFormProps> = ({
|
|||
: dashboardVars.information ?? '',
|
||||
panelGroupMap: dashboardVars.panelGroupMap ?? [],
|
||||
panelGroups: dashboardVars.panelGroups ?? [],
|
||||
applicationMetadataMap: !configure
|
||||
? selectedDashboard.selectedDashboardID !== 'upload'
|
||||
? dashboardVars.applicationMetadataMap
|
||||
: getSelectedApps(selectedDashboard.dashboardJSON)
|
||||
: dashboardVars.applicationMetadataMap ?? [],
|
||||
applicationMetadataMap:
|
||||
!configure && selectedDashboard.selectedDashboardID === 'upload'
|
||||
? getSelectedApps(selectedDashboard.dashboardJSON)
|
||||
: dashboardVars.applicationMetadataMap ?? [],
|
||||
});
|
||||
const [update, setUpdate] = useState(false);
|
||||
const [availableApplicationMetadataMap, setAvailableApplicationMetadataMap] =
|
||||
useState<ApplicationMetadata[]>([]);
|
||||
const [kubeObjInput, setKubeObjInput] = useState<GVRRequest>({
|
||||
group: '',
|
||||
version: 'v1',
|
||||
resource: 'pods',
|
||||
});
|
||||
const [selectedNamespaceList, setSelectedNamespaceList] = useState<
|
||||
Array<Option>
|
||||
>([]);
|
||||
|
||||
// Apollo query to get the agent data
|
||||
const { data: agentList } = useQuery<Clusters, ClusterVars>(GET_CLUSTER, {
|
||||
variables: { project_id: projectID },
|
||||
fetchPolicy: 'cache-and-network',
|
||||
});
|
||||
const { data: agentList, loading } = useQuery<Clusters, ClusterVars>(
|
||||
GET_CLUSTER,
|
||||
{
|
||||
variables: { project_id: projectID },
|
||||
fetchPolicy: 'cache-and-network',
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* GraphQL subscription to fetch the KubeObjData from the server
|
||||
|
@ -242,16 +288,15 @@ const DashboardMetadataForm: React.FC<DashboardMetadataFormProps> = ({
|
|||
}, [update]);
|
||||
|
||||
useEffect(() => {
|
||||
if (dashboardDetails.agentID === '' && !configure) {
|
||||
const availableAgents = (agentList?.getCluster ?? []).filter(
|
||||
(cluster) => {
|
||||
return (
|
||||
cluster.is_active &&
|
||||
cluster.is_cluster_confirmed &&
|
||||
cluster.is_registered
|
||||
);
|
||||
}
|
||||
const availableAgents = (agentList?.getCluster ?? []).filter((cluster) => {
|
||||
return (
|
||||
cluster.is_active &&
|
||||
cluster.is_cluster_confirmed &&
|
||||
cluster.is_registered
|
||||
);
|
||||
});
|
||||
setActiveAgents(availableAgents);
|
||||
if (dashboardDetails.agentID === '' && !configure) {
|
||||
setDashboardDetails({
|
||||
...dashboardDetails,
|
||||
agentID: availableAgents.length ? availableAgents[0].cluster_id : '',
|
||||
|
@ -261,10 +306,11 @@ const DashboardMetadataForm: React.FC<DashboardMetadataFormProps> = ({
|
|||
}, [agentList]);
|
||||
|
||||
useEffect(() => {
|
||||
const availableDataSources = dataSourceList.filter((dataSource) => {
|
||||
return dataSource.health_status === 'Active';
|
||||
});
|
||||
setActiveDataSources(availableDataSources);
|
||||
if (dashboardDetails.dataSourceID === '' && !configure) {
|
||||
const availableDataSources = dataSourceList.filter((dataSource) => {
|
||||
return dataSource.health_status === 'Active';
|
||||
});
|
||||
setDashboardDetails({
|
||||
...dashboardDetails,
|
||||
dataSourceID: availableDataSources.length
|
||||
|
@ -278,28 +324,41 @@ const DashboardMetadataForm: React.FC<DashboardMetadataFormProps> = ({
|
|||
}
|
||||
}, [dataSourceList]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!configure && selectedDashboard.selectedDashboardID === 'upload') {
|
||||
setDashboardDetails({
|
||||
...dashboardDetails,
|
||||
applicationMetadataMap: getSelectedApps(
|
||||
selectedDashboard.dashboardJSON
|
||||
),
|
||||
});
|
||||
}
|
||||
}, [availableApplicationMetadataMap]);
|
||||
|
||||
const getAvailableApplications = () => {
|
||||
const availableApplications: Array<Option> = [];
|
||||
availableApplicationMetadataMap.forEach((appMetadata) => {
|
||||
selectedNamespaceList.forEach((namespaceOption) => {
|
||||
if (namespaceOption.name === appMetadata.namespace) {
|
||||
const apps: Resource[] = appMetadata.applications.filter(
|
||||
(application) => application.kind === kubeObjInput.resource
|
||||
);
|
||||
if (apps.length) {
|
||||
apps[0].names.forEach((appName) => {
|
||||
availableApplications.push({
|
||||
name: `${
|
||||
namespaceOption.name
|
||||
} / ${kubeObjInput.resource.substring(
|
||||
0,
|
||||
kubeObjInput.resource.length - 1
|
||||
)} / ${appName}`,
|
||||
if (selectedNamespaceList.length) {
|
||||
selectedNamespaceList.forEach((namespaceOption) => {
|
||||
if (namespaceOption.name === appMetadata.namespace) {
|
||||
const apps: Resource[] = appMetadata.applications.filter(
|
||||
(application) => application.kind === kubeObjInput.resource
|
||||
);
|
||||
if (apps.length) {
|
||||
apps[0].names.forEach((appName) => {
|
||||
availableApplications.push({
|
||||
name: `${
|
||||
namespaceOption.name
|
||||
} / ${kubeObjInput.resource.substring(
|
||||
0,
|
||||
kubeObjInput.resource.length - 1
|
||||
)} / ${appName}`,
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
return availableApplications;
|
||||
};
|
||||
|
@ -393,21 +452,36 @@ const DashboardMetadataForm: React.FC<DashboardMetadataFormProps> = ({
|
|||
'analyticsDashboard.applicationDashboards.configureDashboardMetadata.form.agent'
|
||||
)}
|
||||
className={classes.selectText}
|
||||
disabled={activeAgents.length === 0}
|
||||
>
|
||||
{(agentList?.getCluster ?? [])
|
||||
.filter((cluster) => {
|
||||
return (
|
||||
cluster.is_active &&
|
||||
cluster.is_cluster_confirmed &&
|
||||
cluster.is_registered
|
||||
);
|
||||
})
|
||||
.map((agent: Cluster) => (
|
||||
<MenuItem key={agent.cluster_id} value={agent.cluster_id}>
|
||||
{agent.cluster_name}
|
||||
</MenuItem>
|
||||
))}
|
||||
{activeAgents.map((agent: Cluster) => (
|
||||
<MenuItem key={agent.cluster_id} value={agent.cluster_id}>
|
||||
{agent.cluster_name}
|
||||
</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
{!activeAgents.length && !loading ? (
|
||||
<Typography className={classes.formErrorText}>
|
||||
{t(
|
||||
'analyticsDashboard.applicationDashboards.configureDashboardMetadata.form.noActiveAgent'
|
||||
)}
|
||||
</Typography>
|
||||
) : !(agentList?.getCluster ?? []).filter((cluster) => {
|
||||
return (
|
||||
cluster.cluster_id === dashboardDetails.agentID &&
|
||||
cluster.is_active &&
|
||||
cluster.is_cluster_confirmed &&
|
||||
cluster.is_registered
|
||||
);
|
||||
}).length ? (
|
||||
<Typography className={classes.formErrorText}>
|
||||
{t(
|
||||
'analyticsDashboard.applicationDashboards.configureDashboardMetadata.form.agentInactive'
|
||||
)}
|
||||
</Typography>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
</FormControl>
|
||||
</div>
|
||||
|
||||
|
@ -436,13 +510,34 @@ const DashboardMetadataForm: React.FC<DashboardMetadataFormProps> = ({
|
|||
'analyticsDashboard.applicationDashboards.configureDashboardMetadata.form.dataSource'
|
||||
)}
|
||||
className={classes.selectText}
|
||||
disabled={activeDataSources.length === 0}
|
||||
>
|
||||
{dataSourceList.map((dataSource: ListDataSourceResponse) => (
|
||||
{activeDataSources.map((dataSource: ListDataSourceResponse) => (
|
||||
<MenuItem key={dataSource.ds_id} value={dataSource.ds_id}>
|
||||
{dataSource.ds_name}
|
||||
</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
{!activeDataSources.length ? (
|
||||
<Typography className={classes.formErrorText}>
|
||||
{t(
|
||||
'analyticsDashboard.applicationDashboards.configureDashboardMetadata.form.noActiveDataSource'
|
||||
)}
|
||||
</Typography>
|
||||
) : !dataSourceList.filter((dataSource) => {
|
||||
return (
|
||||
dataSource.health_status === 'Active' &&
|
||||
dataSource.ds_id === dashboardDetails.dataSourceID
|
||||
);
|
||||
}).length ? (
|
||||
<Typography className={classes.formErrorText}>
|
||||
{t(
|
||||
'analyticsDashboard.applicationDashboards.configureDashboardMetadata.form.dataSourceInactive'
|
||||
)}
|
||||
</Typography>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
</FormControl>
|
||||
|
||||
<InputField
|
||||
|
@ -472,12 +567,19 @@ const DashboardMetadataForm: React.FC<DashboardMetadataFormProps> = ({
|
|||
|
||||
<AutocompleteChipInput
|
||||
defaultValue={getSelectedAppNamespaces()}
|
||||
onChange={(event, value, reason) => {
|
||||
setSelectedNamespaceList(value as Array<Option>);
|
||||
}}
|
||||
options={availableApplicationMetadataMap.map((value) => {
|
||||
return { name: value.namespace };
|
||||
})}
|
||||
onChange={(event, value) =>
|
||||
setSelectedNamespaceList(value as Array<Option>)
|
||||
}
|
||||
getOptionSelected={(option) =>
|
||||
selectedNamespaceList
|
||||
.map((selections) => selections.name)
|
||||
.includes(option.name)
|
||||
}
|
||||
options={
|
||||
availableApplicationMetadataMap.map((value) => {
|
||||
return { name: value.namespace };
|
||||
}) ?? []
|
||||
}
|
||||
label={t(
|
||||
'analyticsDashboard.applicationDashboards.configureDashboardMetadata.form.selectNamespaces'
|
||||
)}
|
||||
|
@ -526,7 +628,7 @@ const DashboardMetadataForm: React.FC<DashboardMetadataFormProps> = ({
|
|||
|
||||
<AutocompleteChipInput
|
||||
defaultValue={getSelectedAppDetails()}
|
||||
onChange={(event, value, reason) => {
|
||||
onChange={(event, value) => {
|
||||
const newSelection: ApplicationMetadata[] = [];
|
||||
const selectedApps: Array<Option> = value as Array<Option>;
|
||||
selectedApps.forEach((nsKindApp) => {
|
||||
|
@ -575,6 +677,11 @@ const DashboardMetadataForm: React.FC<DashboardMetadataFormProps> = ({
|
|||
});
|
||||
setUpdate(true);
|
||||
}}
|
||||
getOptionSelected={(option) =>
|
||||
getSelectedAppDetails()
|
||||
.map((selections) => selections.name)
|
||||
.includes(option.name)
|
||||
}
|
||||
options={getAvailableApplications()}
|
||||
label={t(
|
||||
'analyticsDashboard.applicationDashboards.configureDashboardMetadata.form.selectApplications'
|
||||
|
|
|
@ -41,6 +41,10 @@ const useStyles = makeStyles((theme) => ({
|
|||
height: '3.25rem',
|
||||
},
|
||||
|
||||
formErrorText: {
|
||||
color: theme.palette.error.main,
|
||||
},
|
||||
|
||||
namespaceSelect: {
|
||||
width: '41.5rem',
|
||||
margin: theme.spacing(3.5, 1),
|
||||
|
|
|
@ -128,7 +128,7 @@ const SelectTheMetricsForm: React.FC<SelectTheMetricsFormProps> = ({
|
|||
{panel}
|
||||
</Typography>
|
||||
}
|
||||
key={`metrics-group-${panelGroup.groupName}-label`}
|
||||
key={`metric-${panel}-label`}
|
||||
/>
|
||||
))}
|
||||
</FormGroup>
|
||||
|
|
|
@ -28,7 +28,7 @@ import ace from 'ace-builds';
|
|||
background: rgba(31, 0, 230, 0.15)\
|
||||
}\
|
||||
.lp-code-bright.ace_multiselect .ace_selection.ace_start {\
|
||||
box-shadow: 0 0 3px 0px #272822;\
|
||||
box-shadow: 0 0 3px 0 #272822;\
|
||||
}\
|
||||
.lp-code-bright .ace_marker-layer .ace_step {\
|
||||
background: rgb(102, 82, 0)\
|
||||
|
|
|
@ -355,7 +355,7 @@ const QueryEditor: React.FC<QueryEditorProps> = ({
|
|||
|
||||
<AutocompleteChipInput
|
||||
value={selectedValuesForLabel}
|
||||
onChange={(event, value, reason) => {
|
||||
onChange={(event, value) => {
|
||||
const selectedValues: Array<Option> = value as Array<Option>;
|
||||
const existingLabelValuesList: QueryLabelValue[] =
|
||||
localQuery.labels_and_values_list ?? [];
|
||||
|
@ -386,6 +386,11 @@ const QueryEditor: React.FC<QueryEditorProps> = ({
|
|||
getSelectedValuesForLabel(selectedLabel ?? '');
|
||||
setUpdate(true);
|
||||
}}
|
||||
getOptionSelected={(option) =>
|
||||
selectedValuesForLabel
|
||||
.map((selections) => selections.name)
|
||||
.includes(option.name)
|
||||
}
|
||||
options={getAvailableValues(selectedLabel ?? '')}
|
||||
label={t(
|
||||
'analyticsDashboard.applicationDashboards.tuneTheQueries.values'
|
||||
|
@ -466,8 +471,7 @@ const QueryEditor: React.FC<QueryEditorProps> = ({
|
|||
</Typography>
|
||||
|
||||
<div
|
||||
className={`${classes.flex} ${classes.paddedTop}`}
|
||||
style={{ gap: '2.5rem', width: '98.5%', flexWrap: 'wrap' }}
|
||||
className={`${classes.flex} ${classes.paddedTop} ${classes.configSection}`}
|
||||
>
|
||||
<div className={classes.flex}>
|
||||
<InputField
|
||||
|
@ -564,21 +568,10 @@ const QueryEditor: React.FC<QueryEditorProps> = ({
|
|||
)}
|
||||
</InputLabel>
|
||||
<Select
|
||||
value={
|
||||
localQuery.line
|
||||
? t(
|
||||
'analyticsDashboard.applicationDashboards.tuneTheQueries.lineGraph'
|
||||
)
|
||||
: t(
|
||||
'analyticsDashboard.applicationDashboards.tuneTheQueries.areaGraph'
|
||||
)
|
||||
}
|
||||
value={localQuery.line ? 'Line graph' : 'Area graph'}
|
||||
onChange={(event) => {
|
||||
const line =
|
||||
(event.target.value as string) ===
|
||||
t(
|
||||
'analyticsDashboard.applicationDashboards.tuneTheQueries.areaGraph'
|
||||
);
|
||||
(event.target.value as string) === 'Line graph';
|
||||
setLocalQuery({
|
||||
...localQuery,
|
||||
line,
|
||||
|
@ -595,9 +588,7 @@ const QueryEditor: React.FC<QueryEditorProps> = ({
|
|||
key={`${t(
|
||||
'analyticsDashboard.applicationDashboards.tuneTheQueries.lineGraph'
|
||||
)}`}
|
||||
value={t(
|
||||
'analyticsDashboard.applicationDashboards.tuneTheQueries.lineGraph'
|
||||
)}
|
||||
value="Line graph"
|
||||
>
|
||||
{t(
|
||||
'analyticsDashboard.applicationDashboards.tuneTheQueries.lineGraph'
|
||||
|
@ -607,9 +598,7 @@ const QueryEditor: React.FC<QueryEditorProps> = ({
|
|||
key={`${t(
|
||||
'analyticsDashboard.applicationDashboards.tuneTheQueries.areaGraph'
|
||||
)}`}
|
||||
value={t(
|
||||
'analyticsDashboard.applicationDashboards.tuneTheQueries.areaGraph'
|
||||
)}
|
||||
value="Area graph"
|
||||
>
|
||||
{t(
|
||||
'analyticsDashboard.applicationDashboards.tuneTheQueries.areaGraph'
|
||||
|
|
|
@ -82,6 +82,12 @@ const useStyles = makeStyles((theme) => ({
|
|||
paddingTop: theme.spacing(3.5),
|
||||
},
|
||||
|
||||
configSection: {
|
||||
gap: '2.5rem',
|
||||
width: '98.5%',
|
||||
flexWrap: 'wrap',
|
||||
},
|
||||
|
||||
paddedTop: {
|
||||
paddingTop: theme.spacing(2.5),
|
||||
},
|
||||
|
|
|
@ -9,7 +9,13 @@ import {
|
|||
useTheme,
|
||||
} from '@material-ui/core';
|
||||
import { Autocomplete } from '@material-ui/lab';
|
||||
import { ButtonFilled, ButtonOutlined, EditableText } from 'litmus-ui';
|
||||
import {
|
||||
ButtonFilled,
|
||||
ButtonOutlined,
|
||||
EditableText,
|
||||
Modal,
|
||||
TextButton,
|
||||
} from 'litmus-ui';
|
||||
import moment from 'moment';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
@ -66,6 +72,10 @@ const QueryEditingWizard: React.FC<QueryEditingWizardProps> = ({
|
|||
const theme = useTheme();
|
||||
const { t } = useTranslation();
|
||||
const [tabValue, setTabValue] = React.useState<number>(0);
|
||||
const [discardChangesModalOpen, setDiscardChangesModalOpen] =
|
||||
React.useState<boolean>(false);
|
||||
const [deletePanelModalOpen, setDeletePanelModalOpen] =
|
||||
React.useState<boolean>(false);
|
||||
const [panelInfo, setPanelInfo] = useState<PanelDetails>({ ...panelVars });
|
||||
const [update, setUpdate] = useState<Update>({
|
||||
triggerUpdate: false,
|
||||
|
@ -213,9 +223,7 @@ const QueryEditingWizard: React.FC<QueryEditingWizardProps> = ({
|
|||
style={{ gap: '1rem' }}
|
||||
>
|
||||
<ButtonOutlined
|
||||
onClick={() => {
|
||||
setSettings(!settings);
|
||||
}}
|
||||
onClick={() => setSettings(!settings)}
|
||||
className={classes.iconButton}
|
||||
>
|
||||
<img
|
||||
|
@ -225,9 +233,7 @@ const QueryEditingWizard: React.FC<QueryEditingWizardProps> = ({
|
|||
/>
|
||||
</ButtonOutlined>
|
||||
<ButtonOutlined
|
||||
onClick={() => {
|
||||
handleDeletePanel(index);
|
||||
}}
|
||||
onClick={() => setDeletePanelModalOpen(true)}
|
||||
className={`${classes.iconButton} ${classes.deleteButton}`}
|
||||
>
|
||||
<img
|
||||
|
@ -383,10 +389,8 @@ const QueryEditingWizard: React.FC<QueryEditingWizardProps> = ({
|
|||
)}
|
||||
|
||||
<ButtonFilled
|
||||
onClick={() => {
|
||||
handleDiscardChanges(index);
|
||||
}}
|
||||
className={classes.discardButton}
|
||||
onClick={() => setDiscardChangesModalOpen(true)}
|
||||
variant="error"
|
||||
>
|
||||
<Typography>
|
||||
{t(
|
||||
|
@ -397,6 +401,116 @@ const QueryEditingWizard: React.FC<QueryEditingWizardProps> = ({
|
|||
</div>
|
||||
</>
|
||||
)}
|
||||
|
||||
<Modal
|
||||
open={deletePanelModalOpen}
|
||||
onClose={() => setDeletePanelModalOpen(false)}
|
||||
width="45%"
|
||||
height="fit-content"
|
||||
>
|
||||
<div className={classes.modal}>
|
||||
<Typography className={classes.modalHeading} align="left">
|
||||
<b>
|
||||
{t(
|
||||
'analyticsDashboard.applicationDashboards.tuneTheQueries.removeMetric'
|
||||
)}
|
||||
</b>
|
||||
</Typography>
|
||||
|
||||
<Typography className={classes.modalBodyText} align="left">
|
||||
{t(
|
||||
'analyticsDashboard.applicationDashboards.tuneTheQueries.removeMetricConfirmation'
|
||||
)}
|
||||
<b>
|
||||
<i>{` ${panelInfo.panel_name} `}</i>
|
||||
</b>
|
||||
{t('analyticsDashboard.applicationDashboards.tuneTheQueries.under')}
|
||||
<b>
|
||||
<i>{` ${panelInfo.panel_group_name} `}</i>
|
||||
</b>
|
||||
?
|
||||
</Typography>
|
||||
|
||||
<div className={classes.flexButtons}>
|
||||
<TextButton
|
||||
onClick={() => setDeletePanelModalOpen(false)}
|
||||
className={classes.cancelButton}
|
||||
>
|
||||
<Typography className={classes.buttonText}>
|
||||
{t(
|
||||
'analyticsDashboard.applicationDashboards.tuneTheQueries.cancel'
|
||||
)}
|
||||
</Typography>
|
||||
</TextButton>
|
||||
<ButtonFilled
|
||||
onClick={() => handleDeletePanel(index)}
|
||||
variant="error"
|
||||
>
|
||||
<Typography
|
||||
className={`${classes.buttonText} ${classes.confirmButtonText}`}
|
||||
>
|
||||
{t(
|
||||
'analyticsDashboard.applicationDashboards.tuneTheQueries.delete'
|
||||
)}
|
||||
</Typography>
|
||||
</ButtonFilled>
|
||||
</div>
|
||||
</div>
|
||||
</Modal>
|
||||
|
||||
<Modal
|
||||
open={discardChangesModalOpen}
|
||||
onClose={() => setDiscardChangesModalOpen(false)}
|
||||
width="45%"
|
||||
height="fit-content"
|
||||
>
|
||||
<div className={classes.modal}>
|
||||
<Typography className={classes.modalHeading} align="left">
|
||||
{t(
|
||||
'analyticsDashboard.applicationDashboards.tuneTheQueries.discardChangesConfirmation'
|
||||
)}
|
||||
<b>
|
||||
<i>{` ${panelInfo.panel_name} `}</i>
|
||||
</b>
|
||||
{t('analyticsDashboard.applicationDashboards.tuneTheQueries.under')}
|
||||
<b>
|
||||
<i>{` ${panelInfo.panel_group_name} `}</i>
|
||||
</b>
|
||||
?
|
||||
</Typography>
|
||||
|
||||
<Typography className={classes.modalBodyText} align="left">
|
||||
{t(
|
||||
'analyticsDashboard.applicationDashboards.tuneTheQueries.discardChangesInfo'
|
||||
)}
|
||||
</Typography>
|
||||
|
||||
<div className={classes.flexButtons}>
|
||||
<TextButton
|
||||
onClick={() => setDiscardChangesModalOpen(false)}
|
||||
className={classes.cancelButton}
|
||||
>
|
||||
<Typography className={classes.buttonText}>
|
||||
{t(
|
||||
'analyticsDashboard.applicationDashboards.tuneTheQueries.cancel'
|
||||
)}
|
||||
</Typography>
|
||||
</TextButton>
|
||||
<ButtonFilled
|
||||
onClick={() => handleDiscardChanges(index)}
|
||||
variant="error"
|
||||
>
|
||||
<Typography
|
||||
className={`${classes.buttonText} ${classes.confirmButtonText}`}
|
||||
>
|
||||
{t(
|
||||
'analyticsDashboard.applicationDashboards.tuneTheQueries.yesProceed'
|
||||
)}
|
||||
</Typography>
|
||||
</ButtonFilled>
|
||||
</div>
|
||||
</div>
|
||||
</Modal>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -38,13 +38,6 @@ const useStyles = makeStyles((theme) => ({
|
|||
marginLeft: theme.spacing(1.5),
|
||||
background: theme.palette.cards.header,
|
||||
},
|
||||
discardButton: {
|
||||
background: theme.palette.error.main,
|
||||
'&:hover': {
|
||||
background: theme.palette.error.main,
|
||||
cursor: 'pointer',
|
||||
},
|
||||
},
|
||||
flexBetween: {
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between',
|
||||
|
@ -79,6 +72,40 @@ const useStyles = makeStyles((theme) => ({
|
|||
header: {
|
||||
padding: theme.spacing(1, 2, 4),
|
||||
},
|
||||
|
||||
// modal
|
||||
modalHeading: {
|
||||
fontSize: '1.5rem',
|
||||
lineHeight: '130%',
|
||||
fontFeatureSettings: 'pnum on, lnum on',
|
||||
margin: theme.spacing(2.5, 0, 4.5),
|
||||
padding: theme.spacing(0, 6.5),
|
||||
},
|
||||
modalBodyText: {
|
||||
fontSize: '1rem',
|
||||
lineHeight: '130%',
|
||||
padding: theme.spacing(0, 6.5),
|
||||
},
|
||||
flexButtons: {
|
||||
display: 'flex',
|
||||
justifyContent: 'flex-end',
|
||||
padding: theme.spacing(5.5, 6.5, 0, 0),
|
||||
},
|
||||
modal: {
|
||||
padding: theme.spacing(5, 0),
|
||||
},
|
||||
buttonText: {
|
||||
lineHeight: '140%',
|
||||
fontSize: '0.875rem',
|
||||
},
|
||||
confirmButtonText: {
|
||||
color: theme.palette.text.secondary,
|
||||
padding: theme.spacing(0, 3),
|
||||
},
|
||||
cancelButton: {
|
||||
marginRight: theme.spacing(1.5),
|
||||
padding: theme.spacing(0, 3),
|
||||
},
|
||||
}));
|
||||
|
||||
export default useStyles;
|
||||
|
|
|
@ -14,6 +14,7 @@ import {
|
|||
PanelGroupDetails,
|
||||
PromQueryDetails,
|
||||
} from '../../../../../../models/dashboardsData';
|
||||
import { PanelOption } from '../../../../../../models/graphql/dashboardsDetails';
|
||||
import {
|
||||
PrometheusSeriesListQueryVars,
|
||||
PrometheusSeriesListResponse,
|
||||
|
@ -205,9 +206,7 @@ const EditPanelsWizard: React.FC<EditPanelsWizardProps> = ({
|
|||
setPanelGroupsList(newPanelGroupOptions);
|
||||
};
|
||||
|
||||
const handleCreatePanel = () => {
|
||||
const existingPanels: PanelDetails[] =
|
||||
dashboardDetails.selectedPanels ?? [];
|
||||
const getNewPanel = () => {
|
||||
const newPanel: PanelDetails = {
|
||||
panel_id: '',
|
||||
panel_group_id: '',
|
||||
|
@ -237,6 +236,14 @@ const EditPanelsWizard: React.FC<EditPanelsWizardProps> = ({
|
|||
x_axis_down: '',
|
||||
unit: '',
|
||||
};
|
||||
|
||||
return newPanel;
|
||||
};
|
||||
|
||||
const handleCreatePanel = () => {
|
||||
const existingPanels: PanelDetails[] =
|
||||
dashboardDetails.selectedPanels ?? [];
|
||||
const newPanel = getNewPanel();
|
||||
existingPanels.push(newPanel);
|
||||
setDashboardDetails({
|
||||
...dashboardDetails,
|
||||
|
@ -353,9 +360,12 @@ const EditPanelsWizard: React.FC<EditPanelsWizardProps> = ({
|
|||
dashboardDetails.selectedPanels ?? [];
|
||||
let panelGroupList: PanelGroupDetails[] = [];
|
||||
if (configure) {
|
||||
panelGroupList = dashboardVars.panelGroups ?? [];
|
||||
panelGroupList = dashboardVars.panelGroupMap ?? [];
|
||||
} else {
|
||||
panelGroupList = selectedDashboard.dashboardJSON.panelGroups;
|
||||
panelGroupList =
|
||||
dashboardVars.dashboardTypeID !== 'custom'
|
||||
? selectedDashboard.dashboardJSON.panelGroups
|
||||
: [];
|
||||
}
|
||||
panelGroupList.forEach((panelGroup) => {
|
||||
panelGroup.panels.forEach((selectedPanel) => {
|
||||
|
@ -363,14 +373,32 @@ const EditPanelsWizard: React.FC<EditPanelsWizardProps> = ({
|
|||
configure &&
|
||||
selectedPanel.panel_id === existingPanels[index].panel_id
|
||||
) {
|
||||
const existingPromQueries: PromQueryDetails[] = [];
|
||||
selectedPanel.prom_queries.forEach((promQuery) => {
|
||||
existingPromQueries.push({
|
||||
hidden: false,
|
||||
queryid: promQuery.queryid,
|
||||
prom_query_name: promQuery.prom_query_name,
|
||||
legend: promQuery.legend,
|
||||
resolution: promQuery.resolution,
|
||||
minstep: promQuery.minstep,
|
||||
line: promQuery.line,
|
||||
close_area: promQuery.close_area,
|
||||
});
|
||||
});
|
||||
const existingPanelOptions: PanelOption = {
|
||||
points: selectedPanel.panel_options.points,
|
||||
grids: selectedPanel.panel_options.grids,
|
||||
left_axis: selectedPanel.panel_options.left_axis,
|
||||
};
|
||||
existingPanels[index] = {
|
||||
panel_id: selectedPanel.panel_id ?? '',
|
||||
panel_group_id: panelGroup.panel_group_id ?? '',
|
||||
created_at: selectedPanel.created_at ?? '',
|
||||
panel_group_name: panelGroup.panel_group_name,
|
||||
ds_url: dashboardVars.dataSourceURL ?? '',
|
||||
prom_queries: selectedPanel.prom_queries,
|
||||
panel_options: selectedPanel.panel_options,
|
||||
prom_queries: existingPromQueries,
|
||||
panel_options: existingPanelOptions,
|
||||
panel_name: selectedPanel.panel_name,
|
||||
y_axis_left: selectedPanel.y_axis_left,
|
||||
y_axis_right: selectedPanel.y_axis_right,
|
||||
|
@ -408,6 +436,9 @@ const EditPanelsWizard: React.FC<EditPanelsWizardProps> = ({
|
|||
}
|
||||
});
|
||||
});
|
||||
if (!configure && dashboardVars.dashboardTypeID === 'custom') {
|
||||
existingPanels[index] = getNewPanel();
|
||||
}
|
||||
setDashboardDetails({
|
||||
...dashboardDetails,
|
||||
selectedPanels: existingPanels,
|
||||
|
@ -446,13 +477,18 @@ const EditPanelsWizard: React.FC<EditPanelsWizardProps> = ({
|
|||
scrollButtons="auto"
|
||||
>
|
||||
{dashboardDetails.selectedPanels?.map((panel, index) => (
|
||||
<StyledTab label={panel.panel_name} {...a11yProps(index)} />
|
||||
<StyledTab
|
||||
label={panel.panel_name}
|
||||
{...a11yProps(index)}
|
||||
key={`tab-${panel.panel_group_name}-${panel.panel_name}`}
|
||||
/>
|
||||
))}
|
||||
<StyledTab
|
||||
label={t(
|
||||
'analyticsDashboard.applicationDashboards.tuneTheQueries.addMetric'
|
||||
)}
|
||||
{...a11yProps(dashboardDetails.selectedPanels?.length)}
|
||||
key="tab-addMetric"
|
||||
/>
|
||||
</Tabs>
|
||||
</AppBar>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { useMutation } from '@apollo/client';
|
||||
import { IconButton, Menu, MenuItem, Typography } from '@material-ui/core';
|
||||
import MoreVertIcon from '@material-ui/icons/MoreVert';
|
||||
import { ButtonFilled, ButtonOutlined, Modal } from 'litmus-ui';
|
||||
import { ButtonFilled, Modal, TextButton } from 'litmus-ui';
|
||||
import moment from 'moment';
|
||||
import React, { useEffect } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
@ -13,9 +13,7 @@ import {
|
|||
import useActions from '../../../../redux/actions';
|
||||
import * as DashboardActions from '../../../../redux/actions/dashboards';
|
||||
import * as DataSourceActions from '../../../../redux/actions/dataSource';
|
||||
import * as TabActions from '../../../../redux/actions/tabs';
|
||||
import { history } from '../../../../redux/configureStore';
|
||||
import { ReactComponent as CrossMarkIcon } from '../../../../svg/crossmark.svg';
|
||||
import {
|
||||
getProjectID,
|
||||
getProjectRole,
|
||||
|
@ -24,59 +22,52 @@ import useStyles, { StyledTableCell } from './styles';
|
|||
|
||||
interface TableDataProps {
|
||||
data: ListDashboardResponse;
|
||||
alertStateHandler: (successState: boolean) => void;
|
||||
}
|
||||
|
||||
const TableData: React.FC<TableDataProps> = ({ data }) => {
|
||||
const TableData: React.FC<TableDataProps> = ({ data, alertStateHandler }) => {
|
||||
const classes = useStyles();
|
||||
const { t } = useTranslation();
|
||||
const dashboard = useActions(DashboardActions);
|
||||
const dataSource = useActions(DataSourceActions);
|
||||
const tabs = useActions(TabActions);
|
||||
const projectID = getProjectID();
|
||||
const projectRole = getProjectRole();
|
||||
const [mutate, setMutate] = React.useState(false);
|
||||
const [confirm, setConfirm] = React.useState(false);
|
||||
const [success, setSuccess] = React.useState(false);
|
||||
const [openModal, setOpenModal] = React.useState(false);
|
||||
const [dashboardSelectedForDeleting, setDashboardSelectedForDeleting] =
|
||||
React.useState<DeleteDashboardInput>({
|
||||
dbID: '',
|
||||
});
|
||||
|
||||
const [deleteDashboard] = useMutation<boolean, DeleteDashboardInput>(
|
||||
DELETE_DASHBOARD,
|
||||
{
|
||||
onCompleted: () => {
|
||||
setSuccess(true);
|
||||
setMutate(false);
|
||||
setOpenModal(true);
|
||||
},
|
||||
onError: () => {
|
||||
setMutate(false);
|
||||
setOpenModal(true);
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
// Function to convert UNIX time in format of dddd, DD MMM YYYY, HH:mm
|
||||
const formatDate = (date: string) => {
|
||||
const updated = new Date(parseInt(date, 10) * 1000).toString();
|
||||
const resDate = moment(updated).format('dddd, DD MMM YYYY, HH:mm');
|
||||
return resDate;
|
||||
};
|
||||
|
||||
const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null);
|
||||
|
||||
const open = Boolean(anchorEl);
|
||||
|
||||
const handleClick = (event: React.MouseEvent<HTMLElement>) => {
|
||||
setAnchorEl(event.currentTarget);
|
||||
};
|
||||
|
||||
const handleClose = () => {
|
||||
setAnchorEl(null);
|
||||
};
|
||||
|
||||
const [deleteDashboard] = useMutation<boolean, DeleteDashboardInput>(
|
||||
DELETE_DASHBOARD,
|
||||
{
|
||||
onCompleted: () => {
|
||||
setMutate(false);
|
||||
alertStateHandler(true);
|
||||
},
|
||||
onError: () => {
|
||||
setMutate(false);
|
||||
alertStateHandler(false);
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
const onDashboardLoadRoutine = async () => {
|
||||
dashboard.selectDashboard({
|
||||
selectedDashboardID: data.db_id,
|
||||
|
@ -100,40 +91,65 @@ const TableData: React.FC<TableDataProps> = ({ data }) => {
|
|||
|
||||
return (
|
||||
<>
|
||||
<StyledTableCell className={classes.dashboardName}>
|
||||
<StyledTableCell className={classes.columnDivider}>
|
||||
<Typography
|
||||
variant="body2"
|
||||
align="center"
|
||||
className={classes.tableData}
|
||||
className={`${classes.tableObjects} ${classes.dashboardNameCol} ${classes.dashboardNameColData}`}
|
||||
onClick={() => {
|
||||
onDashboardLoadRoutine().then(() => {
|
||||
history.push({
|
||||
pathname: '/analytics/application-dashboard',
|
||||
search: `?projectID=${projectID}&projectRole=${projectRole}`,
|
||||
});
|
||||
});
|
||||
}}
|
||||
>
|
||||
<strong>{data.db_name}</strong>
|
||||
{data.db_name}
|
||||
</Typography>
|
||||
</StyledTableCell>
|
||||
|
||||
<StyledTableCell className={classes.tableHeader}>
|
||||
<StyledTableCell className={classes.dividerPadding}>
|
||||
<Typography
|
||||
variant="body2"
|
||||
align="center"
|
||||
className={classes.tableData}
|
||||
className={classes.tableObjects}
|
||||
style={{ maxWidth: '5rem' }}
|
||||
>
|
||||
<strong>{data.cluster_name}</strong>
|
||||
{data.cluster_name}
|
||||
</Typography>
|
||||
</StyledTableCell>
|
||||
|
||||
<StyledTableCell className={classes.tableHeader}>
|
||||
<Typography variant="body2" align="center">
|
||||
<strong>{data.db_type_name}</strong>
|
||||
<StyledTableCell>
|
||||
<Typography
|
||||
className={classes.tableObjects}
|
||||
style={{ maxWidth: '7rem' }}
|
||||
>
|
||||
<img
|
||||
src={`/icons/${data.db_type_id}_dashboard.svg`}
|
||||
alt={data.db_type_name}
|
||||
className={classes.inlineTypeIcon}
|
||||
/>
|
||||
{data.db_type_name}
|
||||
</Typography>
|
||||
</StyledTableCell>
|
||||
|
||||
<StyledTableCell className={classes.tableHeader}>
|
||||
<Typography variant="body2" align="center">
|
||||
<strong>{data.ds_type}</strong>
|
||||
<StyledTableCell>
|
||||
<Typography
|
||||
className={classes.tableObjects}
|
||||
style={{ maxWidth: '5rem' }}
|
||||
>
|
||||
<img
|
||||
src="/icons/prometheus.svg"
|
||||
alt="Prometheus"
|
||||
className={classes.inlineIcon}
|
||||
/>
|
||||
{data.ds_type}
|
||||
</Typography>
|
||||
</StyledTableCell>
|
||||
|
||||
<StyledTableCell className={classes.tableHeader}>
|
||||
<Typography variant="body2" align="center">
|
||||
<StyledTableCell>
|
||||
<Typography
|
||||
className={classes.tableObjects}
|
||||
style={{ maxWidth: '12.5rem' }}
|
||||
>
|
||||
<img src="/icons/calendarIcon.svg" alt="Calender" />
|
||||
{formatDate(data.updated_at)}
|
||||
</Typography>
|
||||
</StyledTableCell>
|
||||
|
@ -154,9 +170,19 @@ const TableData: React.FC<TableDataProps> = ({ data }) => {
|
|||
keepMounted
|
||||
open={open}
|
||||
onClose={handleClose}
|
||||
anchorOrigin={{
|
||||
vertical: 'bottom',
|
||||
horizontal: 'right',
|
||||
}}
|
||||
transformOrigin={{
|
||||
vertical: 'top',
|
||||
horizontal: 'right',
|
||||
}}
|
||||
getContentAnchorEl={null}
|
||||
classes={{ paper: classes.menuList }}
|
||||
>
|
||||
<MenuItem
|
||||
value="Analysis"
|
||||
value="View"
|
||||
onClick={() => {
|
||||
onDashboardLoadRoutine().then(() => {
|
||||
history.push({
|
||||
|
@ -169,14 +195,12 @@ const TableData: React.FC<TableDataProps> = ({ data }) => {
|
|||
>
|
||||
<div className={classes.expDiv}>
|
||||
<img
|
||||
src="/icons/analytics.svg"
|
||||
alt="See Analytics"
|
||||
src="/icons/viewAnalytics.svg"
|
||||
alt="View"
|
||||
className={classes.btnImg}
|
||||
/>
|
||||
<Typography data-cy="openDashboard" className={classes.btnText}>
|
||||
{t(
|
||||
'analyticsDashboardViews.kubernetesDashboard.table.seeAnalytics'
|
||||
)}
|
||||
{t('analyticsDashboard.applicationDashboardTable.view')}
|
||||
</Typography>
|
||||
</div>
|
||||
</MenuItem>
|
||||
|
@ -205,9 +229,7 @@ const TableData: React.FC<TableDataProps> = ({ data }) => {
|
|||
data-cy=" configureDashboard"
|
||||
className={classes.btnText}
|
||||
>
|
||||
{t(
|
||||
'analyticsDashboardViews.kubernetesDashboard.table.configure'
|
||||
)}
|
||||
{t('analyticsDashboard.applicationDashboardTable.configure')}
|
||||
</Typography>
|
||||
</div>
|
||||
</MenuItem>
|
||||
|
@ -219,6 +241,7 @@ const TableData: React.FC<TableDataProps> = ({ data }) => {
|
|||
dbID: data.db_id,
|
||||
});
|
||||
setOpenModal(true);
|
||||
handleClose();
|
||||
}}
|
||||
className={classes.menuItem}
|
||||
>
|
||||
|
@ -228,175 +251,62 @@ const TableData: React.FC<TableDataProps> = ({ data }) => {
|
|||
alt="Delete"
|
||||
className={classes.btnImg}
|
||||
/>
|
||||
<Typography data-cy="deleteDashboard" className={classes.btnText}>
|
||||
Delete
|
||||
<Typography
|
||||
data-cy="deleteDashboard"
|
||||
className={`${classes.btnText} ${classes.deleteText}`}
|
||||
>
|
||||
{t('analyticsDashboard.applicationDashboardTable.delete')}
|
||||
</Typography>
|
||||
</div>
|
||||
</MenuItem>
|
||||
</Menu>
|
||||
</StyledTableCell>
|
||||
|
||||
<Modal
|
||||
open={openModal}
|
||||
onClose={() => {
|
||||
setConfirm(false);
|
||||
setOpenModal(false);
|
||||
}}
|
||||
width="60%"
|
||||
modalActions={
|
||||
<ButtonOutlined
|
||||
className={classes.closeButton}
|
||||
onClick={() => {
|
||||
setConfirm(false);
|
||||
setOpenModal(false);
|
||||
}}
|
||||
>
|
||||
✕
|
||||
</ButtonOutlined>
|
||||
}
|
||||
onClose={() => setOpenModal(false)}
|
||||
width="45%"
|
||||
height="fit-content"
|
||||
>
|
||||
<div className={classes.modal}>
|
||||
{confirm === true ? (
|
||||
<Typography align="center">
|
||||
{success === true ? (
|
||||
<img
|
||||
src="/icons/finish.svg"
|
||||
alt="success"
|
||||
className={classes.icon}
|
||||
/>
|
||||
) : (
|
||||
<CrossMarkIcon className={classes.icon} />
|
||||
)}
|
||||
</Typography>
|
||||
) : (
|
||||
<div />
|
||||
)}
|
||||
<Typography className={classes.modalHeading} align="left">
|
||||
{t(
|
||||
'analyticsDashboard.applicationDashboardTable.modal.removeDashboard'
|
||||
)}
|
||||
</Typography>
|
||||
|
||||
{confirm === true ? (
|
||||
<Typography
|
||||
className={classes.modalHeading}
|
||||
align="center"
|
||||
variant="h3"
|
||||
<Typography className={classes.modalBodyText} align="left">
|
||||
{t(
|
||||
'analyticsDashboard.applicationDashboardTable.modal.removeDashboardConfirmation'
|
||||
)}
|
||||
<b>
|
||||
<i>{` ${data.db_name} `}</i>
|
||||
</b>
|
||||
?
|
||||
</Typography>
|
||||
|
||||
<div className={classes.flexButtons}>
|
||||
<TextButton
|
||||
onClick={() => setOpenModal(false)}
|
||||
className={classes.cancelButton}
|
||||
>
|
||||
{success === true
|
||||
? `The dashboard is successfully deleted.`
|
||||
: `There was a problem deleting your dashboard.`}
|
||||
</Typography>
|
||||
) : (
|
||||
<div />
|
||||
)}
|
||||
|
||||
{confirm === true ? (
|
||||
<Typography
|
||||
align="center"
|
||||
variant="body1"
|
||||
className={classes.modalBody}
|
||||
>
|
||||
{success === true ? (
|
||||
<div>
|
||||
You will see the dashboard deleted in the dashboard table.
|
||||
</div>
|
||||
) : (
|
||||
<div>
|
||||
Error encountered while deleting the dashboard. Please try
|
||||
again.
|
||||
</div>
|
||||
)}
|
||||
</Typography>
|
||||
) : (
|
||||
<div />
|
||||
)}
|
||||
|
||||
{success === true && confirm === true ? (
|
||||
<Typography className={classes.buttonText}>
|
||||
{t('analyticsDashboard.applicationDashboardTable.modal.cancel')}
|
||||
</Typography>
|
||||
</TextButton>
|
||||
<ButtonFilled
|
||||
variant="success"
|
||||
onClick={() => {
|
||||
setConfirm(false);
|
||||
setMutate(true);
|
||||
setOpenModal(false);
|
||||
tabs.changeAnalyticsDashboardTabs(2);
|
||||
window.location.reload();
|
||||
}}
|
||||
variant="error"
|
||||
>
|
||||
<div>Back to Kubernetes Dashboard</div>
|
||||
<Typography
|
||||
className={`${classes.buttonText} ${classes.confirmButtonText}`}
|
||||
>
|
||||
{t('analyticsDashboard.applicationDashboardTable.modal.delete')}
|
||||
</Typography>
|
||||
</ButtonFilled>
|
||||
) : success === false && confirm === true ? (
|
||||
<div className={classes.flexButtons}>
|
||||
<ButtonOutlined
|
||||
className={classes.buttonOutlineWarning}
|
||||
onClick={() => {
|
||||
setOpenModal(false);
|
||||
setMutate(true);
|
||||
}}
|
||||
disabled={false}
|
||||
>
|
||||
<div>Try Again</div>
|
||||
</ButtonOutlined>
|
||||
|
||||
<ButtonFilled
|
||||
variant="success"
|
||||
onClick={() => {
|
||||
setConfirm(false);
|
||||
setOpenModal(false);
|
||||
tabs.changeAnalyticsDashboardTabs(2);
|
||||
window.location.reload();
|
||||
}}
|
||||
>
|
||||
<div>Back to Kubernetes Dashboard</div>
|
||||
</ButtonFilled>
|
||||
</div>
|
||||
) : (
|
||||
<div>
|
||||
<Typography align="center">
|
||||
<img
|
||||
src="/icons/delete_large_icon.svg"
|
||||
alt="delete"
|
||||
className={classes.icon}
|
||||
/>
|
||||
</Typography>
|
||||
|
||||
<Typography
|
||||
className={classes.modalHeading}
|
||||
align="center"
|
||||
variant="h3"
|
||||
>
|
||||
Are you sure to remove this dashboard?
|
||||
</Typography>
|
||||
|
||||
<Typography
|
||||
align="center"
|
||||
variant="body1"
|
||||
className={classes.modalBody}
|
||||
>
|
||||
The following action cannot be reverted.
|
||||
</Typography>
|
||||
|
||||
<div className={classes.flexButtons}>
|
||||
<ButtonOutlined
|
||||
onClick={() => {
|
||||
setOpenModal(false);
|
||||
history.push({
|
||||
pathname: '/analytics',
|
||||
search: `?projectID=${projectID}&projectRole=${projectRole}`,
|
||||
});
|
||||
}}
|
||||
disabled={false}
|
||||
>
|
||||
<div>No</div>
|
||||
</ButtonOutlined>
|
||||
|
||||
<ButtonFilled
|
||||
variant="error"
|
||||
onClick={() => {
|
||||
setConfirm(true);
|
||||
setOpenModal(false);
|
||||
setMutate(true);
|
||||
}}
|
||||
>
|
||||
<div>Yes</div>
|
||||
</ButtonFilled>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</Modal>
|
||||
</>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { IconButton, TableHead, TableRow } from '@material-ui/core';
|
||||
import { IconButton, TableHead, TableRow, Typography } from '@material-ui/core';
|
||||
import ExpandLessTwoToneIcon from '@material-ui/icons/ExpandLessTwoTone';
|
||||
import ExpandMoreTwoToneIcon from '@material-ui/icons/ExpandMoreTwoTone';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
|
@ -8,9 +8,6 @@ import useStyles, { StyledTableCell } from './styles';
|
|||
interface SortData {
|
||||
lastViewed: { sort: boolean; ascending: boolean };
|
||||
name: { sort: boolean; ascending: boolean };
|
||||
agent: { sort: boolean; ascending: boolean };
|
||||
dataSourceType: { sort: boolean; ascending: boolean };
|
||||
dashboardType: { sort: boolean; ascending: boolean };
|
||||
}
|
||||
|
||||
interface SortCallBackType {
|
||||
|
@ -28,9 +25,6 @@ const TableHeader: React.FC<TableHeaderProps> = ({ callBackToSort }) => {
|
|||
const [sortData, setSortData] = useState<SortData>({
|
||||
name: { sort: false, ascending: true },
|
||||
lastViewed: { sort: true, ascending: false },
|
||||
agent: { sort: false, ascending: true },
|
||||
dataSourceType: { sort: false, ascending: true },
|
||||
dashboardType: { sort: false, ascending: true },
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
|
@ -39,12 +33,16 @@ const TableHeader: React.FC<TableHeaderProps> = ({ callBackToSort }) => {
|
|||
|
||||
return (
|
||||
<TableHead>
|
||||
<TableRow className={classes.tableHead}>
|
||||
<StyledTableCell className={classes.dashboardName}>
|
||||
<TableRow>
|
||||
<StyledTableCell
|
||||
className={`${classes.headSpacing} ${classes.columnDivider}`}
|
||||
>
|
||||
<div className={classes.nameContent}>
|
||||
<div className={classes.dashboardNameHead}>
|
||||
<b>{t('analyticsDashboard.dashboardTable.tableHead1')}</b>
|
||||
</div>
|
||||
<Typography
|
||||
className={`${classes.dashboardNameHead} ${classes.dashboardNameCol}`}
|
||||
>
|
||||
{t('analyticsDashboard.applicationDashboardTable.tableHead1')}
|
||||
</Typography>
|
||||
<div className={classes.nameContentIcons}>
|
||||
<IconButton
|
||||
aria-label="sort name ascending"
|
||||
|
@ -54,13 +52,10 @@ const TableHeader: React.FC<TableHeaderProps> = ({ callBackToSort }) => {
|
|||
...sortData,
|
||||
name: { sort: true, ascending: true },
|
||||
lastViewed: { sort: false, ascending: true },
|
||||
agent: { sort: false, ascending: true },
|
||||
dataSourceType: { sort: false, ascending: true },
|
||||
dashboardType: { sort: false, ascending: true },
|
||||
})
|
||||
}
|
||||
>
|
||||
<ExpandLessTwoToneIcon className={classes.markerIconUp} />
|
||||
<ExpandLessTwoToneIcon className={classes.markerIcon} />
|
||||
</IconButton>
|
||||
<IconButton
|
||||
aria-label="sort name descending"
|
||||
|
@ -70,145 +65,42 @@ const TableHeader: React.FC<TableHeaderProps> = ({ callBackToSort }) => {
|
|||
...sortData,
|
||||
name: { sort: true, ascending: false },
|
||||
lastViewed: { sort: false, ascending: true },
|
||||
agent: { sort: false, ascending: true },
|
||||
dataSourceType: { sort: false, ascending: true },
|
||||
dashboardType: { sort: false, ascending: true },
|
||||
})
|
||||
}
|
||||
>
|
||||
<ExpandMoreTwoToneIcon className={classes.markerIconDown} />
|
||||
<ExpandMoreTwoToneIcon className={classes.markerIcon} />
|
||||
</IconButton>
|
||||
</div>
|
||||
</div>
|
||||
</StyledTableCell>
|
||||
<StyledTableCell className={classes.tableHeader}>
|
||||
<div className={classes.nameContent}>
|
||||
<div className={classes.dashboardNameHead}>
|
||||
<b>{t('analyticsDashboard.dashboardTable.tableHead2')}</b>
|
||||
</div>
|
||||
<div className={classes.nameContentIcons}>
|
||||
<IconButton
|
||||
aria-label="sort agent name ascending"
|
||||
size="small"
|
||||
onClick={() =>
|
||||
setSortData({
|
||||
...sortData,
|
||||
name: { sort: false, ascending: true },
|
||||
lastViewed: { sort: false, ascending: true },
|
||||
agent: { sort: true, ascending: true },
|
||||
dataSourceType: { sort: false, ascending: true },
|
||||
dashboardType: { sort: false, ascending: true },
|
||||
})
|
||||
}
|
||||
>
|
||||
<ExpandLessTwoToneIcon className={classes.markerIconUp} />
|
||||
</IconButton>
|
||||
<IconButton
|
||||
aria-label="sort agent name descending"
|
||||
size="small"
|
||||
onClick={() =>
|
||||
setSortData({
|
||||
...sortData,
|
||||
name: { sort: false, ascending: true },
|
||||
lastViewed: { sort: false, ascending: true },
|
||||
agent: { sort: true, ascending: false },
|
||||
dataSourceType: { sort: false, ascending: true },
|
||||
dashboardType: { sort: false, ascending: true },
|
||||
})
|
||||
}
|
||||
>
|
||||
<ExpandMoreTwoToneIcon className={classes.markerIconDown} />
|
||||
</IconButton>
|
||||
</div>
|
||||
</div>
|
||||
<StyledTableCell
|
||||
className={`${classes.headSpacing} ${classes.dividerPadding}`}
|
||||
>
|
||||
<Typography
|
||||
className={`${classes.dashboardNameHead} ${classes.dashboardNameHeadWithoutSort}`}
|
||||
>
|
||||
{t('analyticsDashboard.applicationDashboardTable.tableHead2')}
|
||||
</Typography>
|
||||
</StyledTableCell>
|
||||
<StyledTableCell className={classes.tableHeader}>
|
||||
<div className={classes.nameContent}>
|
||||
<div className={classes.dashboardNameHead}>
|
||||
<b>{t('analyticsDashboard.dashboardTable.tableHead3')}</b>
|
||||
</div>
|
||||
<div className={classes.nameContentIcons}>
|
||||
<IconButton
|
||||
aria-label="sort dashboard type ascending"
|
||||
size="small"
|
||||
onClick={() =>
|
||||
setSortData({
|
||||
...sortData,
|
||||
name: { sort: false, ascending: true },
|
||||
lastViewed: { sort: false, ascending: true },
|
||||
agent: { sort: false, ascending: true },
|
||||
dataSourceType: { sort: false, ascending: true },
|
||||
dashboardType: { sort: true, ascending: true },
|
||||
})
|
||||
}
|
||||
>
|
||||
<ExpandLessTwoToneIcon className={classes.markerIconUp} />
|
||||
</IconButton>
|
||||
<IconButton
|
||||
aria-label="sort dashboard type descending"
|
||||
size="small"
|
||||
onClick={() =>
|
||||
setSortData({
|
||||
...sortData,
|
||||
name: { sort: false, ascending: true },
|
||||
lastViewed: { sort: false, ascending: true },
|
||||
agent: { sort: false, ascending: true },
|
||||
dataSourceType: { sort: false, ascending: true },
|
||||
dashboardType: { sort: true, ascending: false },
|
||||
})
|
||||
}
|
||||
>
|
||||
<ExpandMoreTwoToneIcon className={classes.markerIconDown} />
|
||||
</IconButton>
|
||||
</div>
|
||||
</div>
|
||||
<StyledTableCell className={classes.headSpacing}>
|
||||
<Typography
|
||||
className={`${classes.dashboardNameHead} ${classes.dashboardNameHeadWithoutSort}`}
|
||||
>
|
||||
{t('analyticsDashboard.applicationDashboardTable.tableHead3')}
|
||||
</Typography>
|
||||
</StyledTableCell>
|
||||
<StyledTableCell className={classes.tableHeader}>
|
||||
<div className={classes.nameContent}>
|
||||
<div className={classes.dashboardNameHead}>
|
||||
<b>{t('analyticsDashboard.dashboardTable.tableHead4')}</b>
|
||||
</div>
|
||||
<div className={classes.nameContentIcons}>
|
||||
<IconButton
|
||||
aria-label="sort data source type ascending"
|
||||
size="small"
|
||||
onClick={() =>
|
||||
setSortData({
|
||||
...sortData,
|
||||
name: { sort: false, ascending: true },
|
||||
lastViewed: { sort: false, ascending: true },
|
||||
agent: { sort: false, ascending: true },
|
||||
dataSourceType: { sort: true, ascending: true },
|
||||
dashboardType: { sort: false, ascending: true },
|
||||
})
|
||||
}
|
||||
>
|
||||
<ExpandLessTwoToneIcon className={classes.markerIconUp} />
|
||||
</IconButton>
|
||||
<IconButton
|
||||
aria-label="sort data source type descending"
|
||||
size="small"
|
||||
onClick={() =>
|
||||
setSortData({
|
||||
...sortData,
|
||||
name: { sort: false, ascending: true },
|
||||
lastViewed: { sort: false, ascending: true },
|
||||
agent: { sort: false, ascending: true },
|
||||
dataSourceType: { sort: true, ascending: false },
|
||||
dashboardType: { sort: false, ascending: true },
|
||||
})
|
||||
}
|
||||
>
|
||||
<ExpandMoreTwoToneIcon className={classes.markerIconDown} />
|
||||
</IconButton>
|
||||
</div>
|
||||
</div>
|
||||
<StyledTableCell className={classes.headSpacing}>
|
||||
<Typography
|
||||
className={`${classes.dashboardNameHead} ${classes.dashboardNameHeadWithoutSort}`}
|
||||
>
|
||||
{t('analyticsDashboard.applicationDashboardTable.tableHead4')}
|
||||
</Typography>
|
||||
</StyledTableCell>
|
||||
<StyledTableCell className={classes.tableHeader}>
|
||||
<StyledTableCell className={classes.headSpacing}>
|
||||
<div className={classes.nameContent}>
|
||||
<div className={classes.dashboardNameHead}>
|
||||
<b>{t('analyticsDashboard.dashboardTable.tableHead5')}</b>
|
||||
</div>
|
||||
<Typography className={classes.dashboardNameHead}>
|
||||
{t('analyticsDashboard.applicationDashboardTable.tableHead5')}
|
||||
</Typography>
|
||||
<div className={classes.nameContentIcons}>
|
||||
<IconButton
|
||||
aria-label="sort last viewed ascending"
|
||||
|
@ -218,13 +110,10 @@ const TableHeader: React.FC<TableHeaderProps> = ({ callBackToSort }) => {
|
|||
...sortData,
|
||||
name: { sort: false, ascending: true },
|
||||
lastViewed: { sort: true, ascending: true },
|
||||
agent: { sort: false, ascending: true },
|
||||
dataSourceType: { sort: false, ascending: true },
|
||||
dashboardType: { sort: false, ascending: true },
|
||||
})
|
||||
}
|
||||
>
|
||||
<ExpandLessTwoToneIcon className={classes.markerIconUp} />
|
||||
<ExpandLessTwoToneIcon className={classes.markerIcon} />
|
||||
</IconButton>
|
||||
<IconButton
|
||||
aria-label="sort last viewed descending"
|
||||
|
@ -234,13 +123,10 @@ const TableHeader: React.FC<TableHeaderProps> = ({ callBackToSort }) => {
|
|||
...sortData,
|
||||
name: { sort: false, ascending: true },
|
||||
lastViewed: { sort: true, ascending: false },
|
||||
agent: { sort: false, ascending: true },
|
||||
dataSourceType: { sort: false, ascending: true },
|
||||
dashboardType: { sort: false, ascending: true },
|
||||
})
|
||||
}
|
||||
>
|
||||
<ExpandMoreTwoToneIcon className={classes.markerIconDown} />
|
||||
<ExpandMoreTwoToneIcon className={classes.markerIcon} />
|
||||
</IconButton>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -2,18 +2,15 @@ import {
|
|||
Button,
|
||||
FormControl,
|
||||
IconButton,
|
||||
InputAdornment,
|
||||
InputBase,
|
||||
InputLabel,
|
||||
MenuItem,
|
||||
OutlinedInput,
|
||||
Select,
|
||||
Typography,
|
||||
} from '@material-ui/core';
|
||||
import ChevronRightIcon from '@material-ui/icons/ChevronRight';
|
||||
import KeyboardArrowDownIcon from '@material-ui/icons/KeyboardArrowDown';
|
||||
import SearchIcon from '@material-ui/icons/Search';
|
||||
import { ButtonFilled } from 'litmus-ui';
|
||||
import KeyboardArrowUpIcon from '@material-ui/icons/KeyboardArrowUp';
|
||||
import { ButtonFilled, Search } from 'litmus-ui';
|
||||
import React, { ChangeEvent, useRef, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import DateRangeSelector from '../../../../components/DateRangeSelector';
|
||||
|
@ -53,6 +50,7 @@ interface TableToolBarProps {
|
|||
callbackToSetRange: RangeCallBackType;
|
||||
callbackToSetDashboardType: DashboardTypeCallBackType;
|
||||
callbackToSetAgentName: AgentNameCallBackType;
|
||||
createButtonDisabled: boolean;
|
||||
}
|
||||
|
||||
interface RangeType {
|
||||
|
@ -70,6 +68,7 @@ const TableToolBar: React.FC<TableToolBarProps> = ({
|
|||
callbackToSetDataSourceType,
|
||||
callbackToSetDashboardType,
|
||||
callbackToSetAgentName,
|
||||
createButtonDisabled,
|
||||
}) => {
|
||||
const classes = useStyles();
|
||||
const outlinedInputClasses = useOutlinedInputStyles();
|
||||
|
@ -125,145 +124,200 @@ const TableToolBar: React.FC<TableToolBarProps> = ({
|
|||
|
||||
return (
|
||||
<div className={classes.headerSection}>
|
||||
<InputBase
|
||||
id="input-with-icon-adornment"
|
||||
placeholder="Search"
|
||||
className={classes.search}
|
||||
value={searchToken}
|
||||
onChange={handleSearch}
|
||||
classes={{
|
||||
input: classes.input,
|
||||
}}
|
||||
startAdornment={
|
||||
<InputAdornment position="start">
|
||||
<SearchIcon />
|
||||
</InputAdornment>
|
||||
}
|
||||
/>
|
||||
|
||||
<FormControl
|
||||
variant="outlined"
|
||||
className={`${classes.formControl} ${classes.dashboardTypeForm}`}
|
||||
<div className={classes.search}>
|
||||
<Search
|
||||
id="input-with-icon-textfield"
|
||||
placeholder={t('analyticsDashboard.applicationDashboardTable.search')}
|
||||
value={searchToken}
|
||||
onChange={handleSearch}
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
className={classes.headerSection}
|
||||
style={{ justifyContent: 'flex-end' }}
|
||||
>
|
||||
<InputLabel className={classes.selectText}> Agent Name </InputLabel>
|
||||
<Select
|
||||
label="Agent Name"
|
||||
value={agentName}
|
||||
onChange={handleAgentNameChange}
|
||||
className={classes.selectText}
|
||||
input={<OutlinedInput classes={outlinedInputClasses} />}
|
||||
<FormControl
|
||||
variant="outlined"
|
||||
className={`${classes.formControl} ${classes.dashboardTypeForm}`}
|
||||
>
|
||||
<MenuItem value="All">All</MenuItem>
|
||||
{agentNames.map((agentName: string) => (
|
||||
<MenuItem
|
||||
key={`${agentName}-kubernetesDashboard-Toolbar`}
|
||||
value={agentName}
|
||||
>
|
||||
{agentName}
|
||||
<InputLabel className={classes.selectText}>
|
||||
{t('analyticsDashboard.applicationDashboardTable.tableHead2')}
|
||||
</InputLabel>
|
||||
<Select
|
||||
label={t('analyticsDashboard.applicationDashboardTable.tableHead2')}
|
||||
value={agentName}
|
||||
onChange={handleAgentNameChange}
|
||||
className={classes.selectText}
|
||||
input={<OutlinedInput classes={outlinedInputClasses} />}
|
||||
IconComponent={KeyboardArrowDownIcon}
|
||||
MenuProps={{
|
||||
anchorOrigin: {
|
||||
vertical: 'bottom',
|
||||
horizontal: 'right',
|
||||
},
|
||||
transformOrigin: {
|
||||
vertical: 'top',
|
||||
horizontal: 'right',
|
||||
},
|
||||
getContentAnchorEl: null,
|
||||
classes: { paper: classes.menuList },
|
||||
}}
|
||||
>
|
||||
<MenuItem value="All" className={classes.menuListItem}>
|
||||
{t('analyticsDashboard.applicationDashboardTable.all')}
|
||||
</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
</FormControl>
|
||||
{agentNames.map((availableAgentName: string) => (
|
||||
<MenuItem
|
||||
key={`${availableAgentName}-kubernetesDashboard-Toolbar`}
|
||||
value={availableAgentName}
|
||||
className={classes.menuListItem}
|
||||
>
|
||||
{availableAgentName}
|
||||
</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
</FormControl>
|
||||
|
||||
<FormControl
|
||||
variant="outlined"
|
||||
className={`${classes.formControl} ${classes.dashboardTypeForm}`}
|
||||
>
|
||||
<InputLabel className={classes.selectText}> Dashboard Type </InputLabel>
|
||||
<Select
|
||||
label="Dashboard Type"
|
||||
value={dashboardType}
|
||||
onChange={handleDashboardTypeChange}
|
||||
className={classes.selectText}
|
||||
input={<OutlinedInput classes={outlinedInputClasses} />}
|
||||
<FormControl
|
||||
variant="outlined"
|
||||
className={`${classes.formControl} ${classes.dashboardTypeForm}`}
|
||||
>
|
||||
<MenuItem value="All">All</MenuItem>
|
||||
{dashboardTypes.map((dashboardType: string) => (
|
||||
<MenuItem
|
||||
key={`${dashboardType}-kubernetesDashboard-toolbar`}
|
||||
value={dashboardType}
|
||||
>
|
||||
{dashboardType}
|
||||
<InputLabel className={classes.selectText}>
|
||||
{t('analyticsDashboard.applicationDashboardTable.tableHead3')}
|
||||
</InputLabel>
|
||||
<Select
|
||||
label={t('analyticsDashboard.applicationDashboardTable.tableHead3')}
|
||||
value={dashboardType}
|
||||
onChange={handleDashboardTypeChange}
|
||||
className={classes.selectText}
|
||||
input={<OutlinedInput classes={outlinedInputClasses} />}
|
||||
IconComponent={KeyboardArrowDownIcon}
|
||||
MenuProps={{
|
||||
anchorOrigin: {
|
||||
vertical: 'bottom',
|
||||
horizontal: 'right',
|
||||
},
|
||||
transformOrigin: {
|
||||
vertical: 'top',
|
||||
horizontal: 'right',
|
||||
},
|
||||
getContentAnchorEl: null,
|
||||
classes: { paper: classes.menuList },
|
||||
}}
|
||||
>
|
||||
<MenuItem value="All" className={classes.menuListItem}>
|
||||
{t('analyticsDashboard.applicationDashboardTable.all')}
|
||||
</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
</FormControl>
|
||||
{dashboardTypes.map((availableDashboardType: string) => (
|
||||
<MenuItem
|
||||
key={`${availableDashboardType}-kubernetesDashboard-toolbar`}
|
||||
value={availableDashboardType}
|
||||
className={classes.menuListItem}
|
||||
>
|
||||
{availableDashboardType}
|
||||
</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
</FormControl>
|
||||
|
||||
<FormControl
|
||||
variant="outlined"
|
||||
className={`${classes.formControl} ${classes.dataSourceTypeForm}`}
|
||||
>
|
||||
<InputLabel className={classes.selectText}>
|
||||
{' '}
|
||||
Data Source Type{' '}
|
||||
</InputLabel>
|
||||
<Select
|
||||
label="Data Source Type"
|
||||
value={dataSourceType}
|
||||
onChange={handleDataSourceTypeChange}
|
||||
className={classes.selectText}
|
||||
input={<OutlinedInput classes={outlinedInputClasses} />}
|
||||
<FormControl
|
||||
variant="outlined"
|
||||
className={`${classes.formControl} ${classes.dataSourceTypeForm}`}
|
||||
>
|
||||
<MenuItem value="All">All</MenuItem>
|
||||
{dataSourceTypes.map((dataSourceType: string) => (
|
||||
<MenuItem
|
||||
key={`${dataSourceType}-KubernetesDashboard-TableToolbar`}
|
||||
value={dataSourceType}
|
||||
>
|
||||
{dataSourceType}
|
||||
<InputLabel className={classes.selectText}>
|
||||
{t('analyticsDashboard.applicationDashboardTable.tableHead4')}
|
||||
</InputLabel>
|
||||
<Select
|
||||
label={t('analyticsDashboard.applicationDashboardTable.tableHead4')}
|
||||
value={dataSourceType}
|
||||
onChange={handleDataSourceTypeChange}
|
||||
className={classes.selectText}
|
||||
input={<OutlinedInput classes={outlinedInputClasses} />}
|
||||
IconComponent={KeyboardArrowDownIcon}
|
||||
MenuProps={{
|
||||
anchorOrigin: {
|
||||
vertical: 'bottom',
|
||||
horizontal: 'right',
|
||||
},
|
||||
transformOrigin: {
|
||||
vertical: 'top',
|
||||
horizontal: 'right',
|
||||
},
|
||||
getContentAnchorEl: null,
|
||||
classes: { paper: classes.menuList },
|
||||
}}
|
||||
>
|
||||
<MenuItem value="All" className={classes.menuListItem}>
|
||||
{t('analyticsDashboard.applicationDashboardTable.all')}
|
||||
</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
</FormControl>
|
||||
{dataSourceTypes.map((availableDataSourceType: string) => (
|
||||
<MenuItem
|
||||
key={`${availableDataSourceType}-ApplicationDashboards-TableToolbar`}
|
||||
value={availableDataSourceType}
|
||||
className={classes.menuListItem}
|
||||
>
|
||||
{availableDataSourceType}
|
||||
</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
</FormControl>
|
||||
|
||||
<Button
|
||||
className={classes.selectDate}
|
||||
onClick={() => setDateRangeSelectorPopoverOpen(true)}
|
||||
ref={dateRangeSelectorRef}
|
||||
aria-label="time range"
|
||||
aria-haspopup="true"
|
||||
>
|
||||
<Typography className={classes.displayDate}>
|
||||
{range.startDate === ' '
|
||||
? 'Select Period'
|
||||
: `${range.startDate.split(' ')[2]} ${
|
||||
range.startDate.split(' ')[1]
|
||||
} ${range.startDate.split(' ')[3]} - ${
|
||||
range.endDate.split(' ')[2]
|
||||
} ${range.endDate.split(' ')[1]} ${range.endDate.split(' ')[3]}`}
|
||||
<Button
|
||||
className={`${classes.selectDate} ${
|
||||
isDateRangeSelectorPopoverOpen ? classes.selectDateFocused : ''
|
||||
}`}
|
||||
onClick={() => setDateRangeSelectorPopoverOpen(true)}
|
||||
ref={dateRangeSelectorRef}
|
||||
aria-label="time range"
|
||||
aria-haspopup="true"
|
||||
>
|
||||
<Typography className={classes.displayDate}>
|
||||
{range.startDate === ' '
|
||||
? t('analyticsDashboard.applicationDashboardTable.selectPeriod')
|
||||
: `${range.startDate.split(' ')[2]} ${
|
||||
range.startDate.split(' ')[1]
|
||||
} ${range.startDate.split(' ')[3]} - ${
|
||||
range.endDate.split(' ')[2]
|
||||
} ${range.endDate.split(' ')[1]} ${
|
||||
range.endDate.split(' ')[3]
|
||||
}`}
|
||||
|
||||
<IconButton className={classes.rangeSelectorIcon}>
|
||||
{isDateRangeSelectorPopoverOpen ? (
|
||||
<KeyboardArrowDownIcon />
|
||||
) : (
|
||||
<ChevronRightIcon />
|
||||
)}
|
||||
</IconButton>
|
||||
</Typography>
|
||||
</Button>
|
||||
|
||||
<ButtonFilled
|
||||
className={classes.addButton}
|
||||
size="small"
|
||||
onClick={() => {
|
||||
history.push({
|
||||
pathname: '/analytics/dashboard/create',
|
||||
search: `?projectID=${projectID}&projectRole=${projectRole}`,
|
||||
});
|
||||
}}
|
||||
>
|
||||
<Typography className={classes.dateRangeDefault}>
|
||||
{t('analyticsDashboard.dashboardTable.addDashboard')}
|
||||
</Typography>
|
||||
</ButtonFilled>
|
||||
<DateRangeSelector
|
||||
anchorEl={dateRangeSelectorRef.current as HTMLElement}
|
||||
isOpen={isDateRangeSelectorPopoverOpen}
|
||||
onClose={() => {
|
||||
setDateRangeSelectorPopoverOpen(false);
|
||||
}}
|
||||
callbackToSetRange={CallbackFromRangeSelector}
|
||||
/>
|
||||
<IconButton className={classes.rangeSelectorIcon}>
|
||||
{isDateRangeSelectorPopoverOpen ? (
|
||||
<KeyboardArrowUpIcon />
|
||||
) : (
|
||||
<KeyboardArrowDownIcon />
|
||||
)}
|
||||
</IconButton>
|
||||
</Typography>
|
||||
</Button>
|
||||
<ButtonFilled
|
||||
onClick={() =>
|
||||
history.push({
|
||||
pathname: '/analytics/dashboard/create',
|
||||
search: `?projectID=${projectID}&projectRole=${projectRole}`,
|
||||
})
|
||||
}
|
||||
className={classes.createButton}
|
||||
disabled={createButtonDisabled}
|
||||
>
|
||||
<Typography
|
||||
className={`${classes.buttonText} ${
|
||||
createButtonDisabled ? classes.disabledText : ''
|
||||
}`}
|
||||
>
|
||||
{t('analyticsDashboard.applicationDashboardTable.createDashboard')}
|
||||
</Typography>
|
||||
</ButtonFilled>
|
||||
<DateRangeSelector
|
||||
anchorEl={dateRangeSelectorRef.current as HTMLElement}
|
||||
isOpen={isDateRangeSelectorPopoverOpen}
|
||||
onClose={() => {
|
||||
setDateRangeSelectorPopoverOpen(false);
|
||||
}}
|
||||
callbackToSetRange={CallbackFromRangeSelector}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
import { useQuery } from '@apollo/client';
|
||||
import {
|
||||
Paper,
|
||||
Snackbar,
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
|
@ -10,17 +11,30 @@ import {
|
|||
TableRow,
|
||||
Typography,
|
||||
} from '@material-ui/core';
|
||||
import { Alert } from '@material-ui/lab';
|
||||
import { TextButton } from 'litmus-ui';
|
||||
import moment from 'moment';
|
||||
import React from 'react';
|
||||
import React, { useEffect } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import Loader from '../../../../components/Loader';
|
||||
import { LIST_DASHBOARD } from '../../../../graphql/queries';
|
||||
import { LIST_DASHBOARD, LIST_DATASOURCE } from '../../../../graphql/queries';
|
||||
import {
|
||||
DashboardList,
|
||||
ListDashboardResponse,
|
||||
ListDashboardVars,
|
||||
} from '../../../../models/graphql/dashboardsDetails';
|
||||
import { getProjectID } from '../../../../utils/getSearchParams';
|
||||
import {
|
||||
DataSourceList,
|
||||
ListDataSourceResponse,
|
||||
ListDataSourceVars,
|
||||
} from '../../../../models/graphql/dataSourceDetails';
|
||||
import useActions from '../../../../redux/actions';
|
||||
import * as TabActions from '../../../../redux/actions/tabs';
|
||||
import { history } from '../../../../redux/configureStore';
|
||||
import {
|
||||
getProjectID,
|
||||
getProjectRole,
|
||||
} from '../../../../utils/getSearchParams';
|
||||
import {
|
||||
sortAlphaAsc,
|
||||
sortAlphaDesc,
|
||||
|
@ -40,9 +54,6 @@ interface RangeType {
|
|||
interface SortData {
|
||||
lastViewed: { sort: boolean; ascending: boolean };
|
||||
name: { sort: boolean; ascending: boolean };
|
||||
agent: { sort: boolean; ascending: boolean };
|
||||
dataSourceType: { sort: boolean; ascending: boolean };
|
||||
dashboardType: { sort: boolean; ascending: boolean };
|
||||
}
|
||||
|
||||
interface Filter {
|
||||
|
@ -64,9 +75,6 @@ const DashboardTable: React.FC = () => {
|
|||
sortData: {
|
||||
name: { sort: false, ascending: true },
|
||||
lastViewed: { sort: true, ascending: false },
|
||||
agent: { sort: false, ascending: true },
|
||||
dataSourceType: { sort: false, ascending: true },
|
||||
dashboardType: { sort: false, ascending: true },
|
||||
},
|
||||
selectedAgentName: 'All',
|
||||
searchTokens: [''],
|
||||
|
@ -74,6 +82,38 @@ const DashboardTable: React.FC = () => {
|
|||
const [page, setPage] = React.useState(0);
|
||||
const [rowsPerPage, setRowsPerPage] = React.useState(5);
|
||||
const projectID = getProjectID();
|
||||
const projectRole = getProjectRole();
|
||||
const tabs = useActions(TabActions);
|
||||
const [isAlertOpen, setIsAlertOpen] = React.useState(false);
|
||||
const [success, setSuccess] = React.useState(false);
|
||||
const [activeDataSourceAvailable, setActiveDataSourceAvailable] =
|
||||
React.useState(false);
|
||||
|
||||
// Apollo query to get the dashboard data
|
||||
const { data, loading, error, refetch } = useQuery<
|
||||
DashboardList,
|
||||
ListDashboardVars
|
||||
>(LIST_DASHBOARD, {
|
||||
variables: { projectID },
|
||||
fetchPolicy: 'cache-and-network',
|
||||
});
|
||||
|
||||
// Apollo query to get the data source data
|
||||
const { data: dataSourceList, loading: loadingDataSources } = useQuery<
|
||||
DataSourceList,
|
||||
ListDataSourceVars
|
||||
>(LIST_DATASOURCE, {
|
||||
variables: { projectID },
|
||||
fetchPolicy: 'cache-and-network',
|
||||
});
|
||||
|
||||
const alertStateHandler = (successState: boolean) => {
|
||||
setSuccess(successState);
|
||||
setIsAlertOpen(true);
|
||||
if (successState) {
|
||||
refetch();
|
||||
}
|
||||
};
|
||||
|
||||
const handleChangePage = (event: unknown, newPage: number) => {
|
||||
setPage(newPage);
|
||||
|
@ -86,15 +126,6 @@ const DashboardTable: React.FC = () => {
|
|||
setPage(0);
|
||||
};
|
||||
|
||||
// Apollo query to get the dashboard data
|
||||
const { data, loading, error } = useQuery<DashboardList, ListDashboardVars>(
|
||||
LIST_DASHBOARD,
|
||||
{
|
||||
variables: { projectID },
|
||||
fetchPolicy: 'cache-and-network',
|
||||
}
|
||||
);
|
||||
|
||||
const getDataSourceType = (searchingData: ListDashboardResponse[]) => {
|
||||
const uniqueList: string[] = [];
|
||||
searchingData.forEach((data) => {
|
||||
|
@ -108,8 +139,8 @@ const DashboardTable: React.FC = () => {
|
|||
const getDashboardType = (searchingData: ListDashboardResponse[]) => {
|
||||
const uniqueList: string[] = [];
|
||||
searchingData.forEach((data) => {
|
||||
if (!uniqueList.includes(data.db_type)) {
|
||||
uniqueList.push(data.db_type);
|
||||
if (!uniqueList.includes(data.db_type_name)) {
|
||||
uniqueList.push(data.db_type_name);
|
||||
}
|
||||
});
|
||||
return uniqueList;
|
||||
|
@ -129,12 +160,8 @@ const DashboardTable: React.FC = () => {
|
|||
? !data.ListDashboard
|
||||
? []
|
||||
: data.ListDashboard.filter((db: ListDashboardResponse) => {
|
||||
return filter.searchTokens.every(
|
||||
(s: string) =>
|
||||
db.db_name.toLowerCase().includes(s) ||
|
||||
db.db_type.toLowerCase().includes(s) ||
|
||||
db.ds_type.toLowerCase().includes(s) ||
|
||||
db.cluster_name.toLowerCase().includes(s)
|
||||
return filter.searchTokens.every((s: string) =>
|
||||
db.db_name.toLowerCase().includes(s)
|
||||
);
|
||||
})
|
||||
.filter((data) => {
|
||||
|
@ -145,7 +172,7 @@ const DashboardTable: React.FC = () => {
|
|||
.filter((data) => {
|
||||
return filter.selectedDashboardType === 'All'
|
||||
? true
|
||||
: data.db_type === filter.selectedDashboardType;
|
||||
: data.db_type_name === filter.selectedDashboardType;
|
||||
})
|
||||
.filter((data) => {
|
||||
return filter.selectedAgentName === 'All'
|
||||
|
@ -183,33 +210,79 @@ const DashboardTable: React.FC = () => {
|
|||
? sortNumAsc(y, x)
|
||||
: sortNumDesc(y, x);
|
||||
}
|
||||
if (filter.sortData.dashboardType.sort) {
|
||||
const x = a.db_type;
|
||||
const y = b.db_type;
|
||||
return filter.sortData.dashboardType.ascending
|
||||
? sortAlphaAsc(x, y)
|
||||
: sortAlphaDesc(x, y);
|
||||
}
|
||||
if (filter.sortData.dataSourceType.sort) {
|
||||
const x = a.ds_type;
|
||||
const y = b.ds_type;
|
||||
return filter.sortData.dataSourceType.ascending
|
||||
? sortAlphaAsc(x, y)
|
||||
: sortAlphaDesc(x, y);
|
||||
}
|
||||
if (filter.sortData.agent.sort) {
|
||||
const x = a.cluster_name;
|
||||
const y = b.cluster_name;
|
||||
return filter.sortData.agent.ascending
|
||||
? sortAlphaAsc(x, y)
|
||||
: sortAlphaDesc(x, y);
|
||||
}
|
||||
return 0;
|
||||
})
|
||||
: [];
|
||||
|
||||
useEffect(() => {
|
||||
if (dataSourceList && dataSourceList.ListDataSource) {
|
||||
const activeDataSources: ListDataSourceResponse[] =
|
||||
dataSourceList.ListDataSource.filter(
|
||||
(dataSource) => dataSource.health_status === 'Active'
|
||||
) ?? [];
|
||||
if (activeDataSources.length) {
|
||||
setActiveDataSourceAvailable(true);
|
||||
}
|
||||
}
|
||||
}, [dataSourceList]);
|
||||
|
||||
return (
|
||||
<div className={classes.root}>
|
||||
{!activeDataSourceAvailable && !loadingDataSources && (
|
||||
<blockquote className={classes.warningBlock}>
|
||||
<Typography className={classes.warningText} align="left">
|
||||
{dataSourceList?.ListDataSource.length
|
||||
? t(
|
||||
'analyticsDashboard.applicationDashboardTable.warning.noActiveDataSource'
|
||||
)
|
||||
: t(
|
||||
'analyticsDashboard.applicationDashboardTable.warning.noAvailableDataSource'
|
||||
)}
|
||||
</Typography>
|
||||
<div className={classes.warningActions}>
|
||||
{dataSourceList && dataSourceList.ListDataSource.length > 0 && (
|
||||
<>
|
||||
<TextButton
|
||||
onClick={() => tabs.changeAnalyticsDashboardTabs(3)}
|
||||
variant="highlight"
|
||||
className={classes.warningButton}
|
||||
>
|
||||
<Typography
|
||||
className={classes.buttonText}
|
||||
style={{ fontWeight: 500 }}
|
||||
>
|
||||
{t(
|
||||
'analyticsDashboard.applicationDashboardTable.warning.configureExisting'
|
||||
)}
|
||||
</Typography>
|
||||
</TextButton>
|
||||
<Typography className={classes.orText}>
|
||||
{t('analyticsDashboard.applicationDashboardTable.warning.or')}
|
||||
</Typography>
|
||||
</>
|
||||
)}
|
||||
<TextButton
|
||||
onClick={() =>
|
||||
history.push({
|
||||
pathname: '/analytics/datasource/create',
|
||||
search: `?projectID=${projectID}&projectRole=${projectRole}`,
|
||||
})
|
||||
}
|
||||
variant="highlight"
|
||||
className={classes.warningButton}
|
||||
>
|
||||
<Typography
|
||||
className={classes.buttonText}
|
||||
style={{ fontWeight: 500 }}
|
||||
>
|
||||
{t(
|
||||
'analyticsDashboard.applicationDashboardTable.warning.addNew'
|
||||
)}
|
||||
</Typography>
|
||||
</TextButton>
|
||||
</div>
|
||||
</blockquote>
|
||||
)}
|
||||
<Paper>
|
||||
<section className="Heading section">
|
||||
<TableToolBar
|
||||
|
@ -229,53 +302,60 @@ const DashboardTable: React.FC = () => {
|
|||
.filter((s) => s !== ''),
|
||||
})
|
||||
}
|
||||
dataSourceTypes={getDataSourceType(payload)}
|
||||
dashboardTypes={getDashboardType(payload)}
|
||||
agentNames={getAgentName(payload)}
|
||||
callbackToSetDataSourceType={(dataSourceType: string) => {
|
||||
dataSourceTypes={getDataSourceType(data?.ListDashboard ?? [])}
|
||||
dashboardTypes={getDashboardType(data?.ListDashboard ?? [])}
|
||||
agentNames={getAgentName(data?.ListDashboard ?? [])}
|
||||
callbackToSetDataSourceType={(dataSourceType: string) =>
|
||||
setFilter({
|
||||
...filter,
|
||||
selectedDataSourceType: dataSourceType,
|
||||
});
|
||||
}}
|
||||
callbackToSetDashboardType={(dashboardType: string) => {
|
||||
})
|
||||
}
|
||||
callbackToSetDashboardType={(dashboardType: string) =>
|
||||
setFilter({
|
||||
...filter,
|
||||
selectedDashboardType: dashboardType,
|
||||
});
|
||||
}}
|
||||
callbackToSetAgentName={(agentName: string) => {
|
||||
})
|
||||
}
|
||||
callbackToSetAgentName={(agentName: string) =>
|
||||
setFilter({
|
||||
...filter,
|
||||
selectedAgentName: agentName,
|
||||
});
|
||||
}}
|
||||
})
|
||||
}
|
||||
callbackToSetRange={(
|
||||
selectedStartDate: string,
|
||||
selectedEndDate: string
|
||||
) => {
|
||||
) =>
|
||||
setFilter({
|
||||
...filter,
|
||||
range: {
|
||||
startDate: selectedStartDate,
|
||||
endDate: selectedEndDate,
|
||||
},
|
||||
});
|
||||
}}
|
||||
})
|
||||
}
|
||||
createButtonDisabled={
|
||||
!activeDataSourceAvailable && !loadingDataSources
|
||||
}
|
||||
/>
|
||||
</section>
|
||||
</Paper>
|
||||
<Paper>
|
||||
<section className="table section">
|
||||
<TableContainer className={classes.tableMain}>
|
||||
<TableContainer
|
||||
className={`${classes.tableMain} ${
|
||||
!payload.length || loading ? classes.minHeight : ''
|
||||
}`}
|
||||
>
|
||||
<Table aria-label="simple table">
|
||||
<TableHeader
|
||||
callBackToSort={(sortConfigurations: SortData) => {
|
||||
callBackToSort={(sortConfigurations: SortData) =>
|
||||
setFilter({
|
||||
...filter,
|
||||
sortData: sortConfigurations,
|
||||
});
|
||||
}}
|
||||
})
|
||||
}
|
||||
/>
|
||||
<TableBody>
|
||||
{error ? (
|
||||
|
@ -283,7 +363,7 @@ const DashboardTable: React.FC = () => {
|
|||
<TableCell colSpan={6}>
|
||||
<Typography align="center">
|
||||
{t(
|
||||
'analyticsDashboardViews.kubernetesDashboard.table.error'
|
||||
'analyticsDashboard.applicationDashboardTable.error'
|
||||
)}
|
||||
</Typography>
|
||||
</TableCell>
|
||||
|
@ -291,22 +371,33 @@ const DashboardTable: React.FC = () => {
|
|||
) : loading ? (
|
||||
<TableRow>
|
||||
<TableCell colSpan={6}>
|
||||
<Loader />
|
||||
<Typography align="center">
|
||||
{t(
|
||||
'analyticsDashboardViews.kubernetesDashboard.table.loading'
|
||||
)}
|
||||
</Typography>
|
||||
<div
|
||||
className={`${classes.noRecords} ${classes.loading}`}
|
||||
>
|
||||
<Loader />
|
||||
<Typography align="center">
|
||||
{t(
|
||||
'analyticsDashboard.applicationDashboardTable.loading'
|
||||
)}
|
||||
</Typography>
|
||||
</div>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
) : !payload.length ? (
|
||||
<TableRow>
|
||||
<TableCell colSpan={6}>
|
||||
<Typography align="center">
|
||||
{t(
|
||||
'analyticsDashboardViews.kubernetesDashboard.table.noRecords'
|
||||
)}
|
||||
</Typography>
|
||||
<div className={classes.noRecords}>
|
||||
<img
|
||||
src="/icons/dashboardUnavailable.svg"
|
||||
className={classes.unavailableIcon}
|
||||
alt="Dashboard"
|
||||
/>
|
||||
<Typography className={classes.noRecordsText}>
|
||||
{t(
|
||||
'analyticsDashboard.applicationDashboardTable.noRecords'
|
||||
)}
|
||||
</Typography>
|
||||
</div>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
) : payload.length > 0 ? (
|
||||
|
@ -321,7 +412,10 @@ const DashboardTable: React.FC = () => {
|
|||
key={data.db_id}
|
||||
className={classes.tableRow}
|
||||
>
|
||||
<TableData data={data} />
|
||||
<TableData
|
||||
data={data}
|
||||
alertStateHandler={alertStateHandler}
|
||||
/>
|
||||
</TableRow>
|
||||
);
|
||||
})
|
||||
|
@ -330,7 +424,7 @@ const DashboardTable: React.FC = () => {
|
|||
<TableCell colSpan={6}>
|
||||
<Typography align="center">
|
||||
{t(
|
||||
'analyticsDashboardViews.kubernetesDashboard.table.noRecords'
|
||||
'analyticsDashboard.applicationDashboardTable.noRecords'
|
||||
)}
|
||||
</Typography>
|
||||
</TableCell>
|
||||
|
@ -348,9 +442,42 @@ const DashboardTable: React.FC = () => {
|
|||
onChangePage={handleChangePage}
|
||||
onChangeRowsPerPage={handleChangeRowsPerPage}
|
||||
className={classes.tablePagination}
|
||||
SelectProps={{
|
||||
MenuProps: {
|
||||
anchorOrigin: {
|
||||
vertical: 'bottom',
|
||||
horizontal: 'right',
|
||||
},
|
||||
transformOrigin: {
|
||||
vertical: 'top',
|
||||
horizontal: 'right',
|
||||
},
|
||||
getContentAnchorEl: null,
|
||||
classes: { paper: classes.menuList },
|
||||
},
|
||||
}}
|
||||
classes={{ menuItem: classes.menuListItem }}
|
||||
/>
|
||||
</section>
|
||||
</Paper>
|
||||
{isAlertOpen && (
|
||||
<Snackbar
|
||||
open={isAlertOpen}
|
||||
autoHideDuration={3000}
|
||||
onClose={() => setIsAlertOpen(false)}
|
||||
>
|
||||
<Alert
|
||||
onClose={() => setIsAlertOpen(false)}
|
||||
severity={success ? 'success' : 'error'}
|
||||
>
|
||||
{success
|
||||
? t(
|
||||
'analyticsDashboard.applicationDashboardTable.deletionSuccess'
|
||||
)
|
||||
: t('analyticsDashboard.applicationDashboardTable.deletionError')}
|
||||
</Alert>
|
||||
</Snackbar>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -15,37 +15,28 @@ const useStyles = makeStyles((theme) => ({
|
|||
marginTop: theme.spacing(3),
|
||||
},
|
||||
|
||||
headerSection: {
|
||||
width: '100%',
|
||||
warningBlock: {
|
||||
margin: theme.spacing(0, 0, 4),
|
||||
padding: theme.spacing(0.5, 3),
|
||||
borderLeft: `5px solid ${theme.palette.warning.main}`,
|
||||
borderRadius: '5px 0 0 5px',
|
||||
background: theme.palette.warning.light,
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
alignContent: 'center',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
height: '6rem',
|
||||
backgroundColor: theme.palette.background.paper,
|
||||
justifyContent: 'space-between',
|
||||
},
|
||||
|
||||
headerIcon: {
|
||||
color: theme.palette.text.primary,
|
||||
opacity: 0.6,
|
||||
warningText: {
|
||||
margin: theme.spacing(1.5, 0),
|
||||
fontSize: '1rem',
|
||||
lineHeight: '130%',
|
||||
fontFeatureSettings: 'pnum on, lnum on',
|
||||
},
|
||||
|
||||
input: {
|
||||
'&:-webkit-autofill': {
|
||||
WebkitTextFillColor: theme.palette.text.primary,
|
||||
WebkitBoxShadow: `0 0 0 1000px ${theme.palette.background.paper} inset`,
|
||||
},
|
||||
warningActions: {
|
||||
display: 'flex',
|
||||
margin: theme.spacing(1.5),
|
||||
},
|
||||
|
||||
tableRow: {
|
||||
height: '4.5rem',
|
||||
},
|
||||
|
||||
search: {
|
||||
marginRight: 'auto',
|
||||
marginLeft: theme.spacing(6.25),
|
||||
borderBottom: `1px solid ${theme.palette.border.main}`,
|
||||
warningButton: {
|
||||
padding: 0,
|
||||
minHeight: 0,
|
||||
},
|
||||
|
||||
tableMain: {
|
||||
|
@ -55,6 +46,7 @@ const useStyles = makeStyles((theme) => ({
|
|||
minHeight: '25rem',
|
||||
'&::-webkit-scrollbar': {
|
||||
width: '0.2em',
|
||||
height: '0.2em',
|
||||
},
|
||||
'&::-webkit-scrollbar-track': {
|
||||
webkitBoxShadow: `inset 0 0 6px ${theme.palette.common.black}`,
|
||||
|
@ -67,28 +59,83 @@ const useStyles = makeStyles((theme) => ({
|
|||
},
|
||||
},
|
||||
|
||||
tableHead: {
|
||||
opacity: 0.7,
|
||||
color: theme.palette.text.primary,
|
||||
tableRow: {
|
||||
height: '4.5rem',
|
||||
},
|
||||
|
||||
dashboardName: {
|
||||
borderRight: `1px solid ${theme.palette.border.main}`,
|
||||
minWidth: '10rem',
|
||||
paddingLeft: theme.spacing(5),
|
||||
headerSection: {
|
||||
width: '100%',
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
alignContent: 'center',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between',
|
||||
overflowX: 'auto',
|
||||
height: '6rem',
|
||||
backgroundColor: theme.palette.background.paper,
|
||||
},
|
||||
|
||||
tableHeader: {
|
||||
minWidth: '12rem',
|
||||
search: {
|
||||
marginLeft: theme.spacing(6),
|
||||
},
|
||||
|
||||
minHeight: {
|
||||
height: '20rem',
|
||||
minHeight: '20rem',
|
||||
},
|
||||
|
||||
noRecords: {
|
||||
height: '12.5rem',
|
||||
display: 'flex',
|
||||
padding: theme.spacing(7.5, 3, 5),
|
||||
justifyContent: 'center',
|
||||
},
|
||||
|
||||
loading: {
|
||||
flexDirection: 'column',
|
||||
padding: theme.spacing(2, 3, 7.5),
|
||||
},
|
||||
|
||||
unavailableIcon: {
|
||||
height: '3.5rem',
|
||||
width: '3.5rem',
|
||||
marginTop: theme.spacing(0.65),
|
||||
},
|
||||
|
||||
noRecordsText: {
|
||||
color: theme.palette.text.hint,
|
||||
padding: theme.spacing(2),
|
||||
fontSize: '1.5rem',
|
||||
lineHeight: '150%',
|
||||
letterSpacing: '0.1176px',
|
||||
textAlign: 'center',
|
||||
},
|
||||
|
||||
dashboardNameHead: {
|
||||
margin: '0 auto',
|
||||
marginTop: theme.spacing(2.5),
|
||||
fontWeight: 500,
|
||||
fontSize: '0.75rem',
|
||||
lineHeight: '150%',
|
||||
color: theme.palette.text.hint,
|
||||
},
|
||||
|
||||
dashboardType: {
|
||||
marginTop: theme.spacing(2.5),
|
||||
dashboardNameHeadWithoutSort: {
|
||||
marginTop: theme.spacing(0.75),
|
||||
},
|
||||
|
||||
dashboardNameCol: {
|
||||
paddingLeft: theme.spacing(4),
|
||||
},
|
||||
|
||||
dashboardNameColData: {
|
||||
maxWidth: '10rem',
|
||||
fontWeight: 500,
|
||||
cursor: 'pointer',
|
||||
},
|
||||
|
||||
headSpacing: {
|
||||
minWidth: '5rem',
|
||||
paddingLeft: theme.spacing(2),
|
||||
},
|
||||
|
||||
nameContent: {
|
||||
|
@ -102,43 +149,17 @@ const useStyles = makeStyles((theme) => ({
|
|||
justifyContent: 'center',
|
||||
},
|
||||
|
||||
markerIconDown: {
|
||||
markerIcon: {
|
||||
color: theme.palette.text.hint,
|
||||
paddingTop: theme.spacing(0.5),
|
||||
margin: 0,
|
||||
},
|
||||
|
||||
markerIconUp: {
|
||||
color: theme.palette.text.hint,
|
||||
paddingTop: theme.spacing(0.5),
|
||||
margin: 0,
|
||||
},
|
||||
|
||||
tableData: {
|
||||
paddingRight: theme.spacing(2),
|
||||
},
|
||||
|
||||
options: {
|
||||
minWidth: '4rem',
|
||||
minWidth: '3rem',
|
||||
paddingRight: theme.spacing(2),
|
||||
},
|
||||
|
||||
// Menu option with icon
|
||||
expDiv: {
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
},
|
||||
|
||||
btnImg: {
|
||||
width: '0.8125rem',
|
||||
height: '0.8125rem',
|
||||
marginTop: theme.spacing(0.375),
|
||||
},
|
||||
|
||||
btnText: {
|
||||
paddingLeft: theme.spacing(1.625),
|
||||
},
|
||||
|
||||
tablePagination: {
|
||||
marginTop: theme.spacing(-0.25),
|
||||
borderTop: `1px solid ${theme.palette.border.main}`,
|
||||
|
@ -152,12 +173,19 @@ const useStyles = makeStyles((theme) => ({
|
|||
|
||||
selectDate: {
|
||||
display: 'flex',
|
||||
height: '2.9rem',
|
||||
height: '2.75rem',
|
||||
minWidth: '9rem',
|
||||
border: `0.1px solid ${theme.palette.border.main}`,
|
||||
borderRadius: 4,
|
||||
borderRadius: '0.25rem',
|
||||
marginRight: theme.spacing(1.5),
|
||||
textTransform: 'none',
|
||||
'&:hover': {
|
||||
borderColor: theme.palette.highlight,
|
||||
},
|
||||
},
|
||||
|
||||
selectDateFocused: {
|
||||
border: `2px solid ${theme.palette.highlight}`,
|
||||
},
|
||||
|
||||
rangeSelectorIcon: {
|
||||
|
@ -165,10 +193,38 @@ const useStyles = makeStyles((theme) => ({
|
|||
height: '0.625rem',
|
||||
},
|
||||
|
||||
tableObjects: {
|
||||
display: 'flex',
|
||||
gap: '0.5rem',
|
||||
textAlign: 'left',
|
||||
color: theme.palette.text.primary,
|
||||
fontSize: '0.75rem',
|
||||
lineHeight: '150%',
|
||||
},
|
||||
|
||||
inlineIcon: {
|
||||
margin: theme.spacing(0.25, 0),
|
||||
width: '1rem',
|
||||
height: '1rem',
|
||||
},
|
||||
|
||||
inlineTypeIcon: {
|
||||
width: '1.25rem',
|
||||
height: '1.25rem',
|
||||
},
|
||||
|
||||
columnDivider: {
|
||||
borderRight: `1px solid ${theme.palette.border.main}`,
|
||||
},
|
||||
|
||||
dividerPadding: {
|
||||
paddingLeft: theme.spacing(4),
|
||||
},
|
||||
|
||||
// Form Select Properties
|
||||
formControl: {
|
||||
margin: theme.spacing(0.5),
|
||||
height: '2.8rem',
|
||||
height: '2.6rem',
|
||||
minWidth: '9rem',
|
||||
},
|
||||
|
||||
|
@ -186,50 +242,97 @@ const useStyles = makeStyles((theme) => ({
|
|||
padding: theme.spacing(0.4),
|
||||
},
|
||||
|
||||
createButton: {
|
||||
margin: theme.spacing(0, 3, 0, 1),
|
||||
padding: theme.spacing(0, 2.5),
|
||||
},
|
||||
|
||||
// Menu option with icon
|
||||
menuItem: {
|
||||
width: '10rem',
|
||||
height: '2.5rem',
|
||||
'&:hover': {
|
||||
background: theme.palette.cards.highlight,
|
||||
},
|
||||
},
|
||||
|
||||
dateRangeDefault: {
|
||||
padding: theme.spacing(1),
|
||||
headerIcon: {
|
||||
color: theme.palette.border.main,
|
||||
},
|
||||
|
||||
addButton: {
|
||||
margin: theme.spacing(0, 2),
|
||||
},
|
||||
|
||||
icon: {
|
||||
width: '6rem',
|
||||
height: '6rem',
|
||||
},
|
||||
|
||||
modalHeading: {
|
||||
marginTop: theme.spacing(3.5),
|
||||
fontSize: '2.25rem',
|
||||
marginBottom: theme.spacing(4.5),
|
||||
},
|
||||
|
||||
modalBody: {
|
||||
marginBottom: theme.spacing(4.5),
|
||||
},
|
||||
|
||||
closeButton: {
|
||||
borderColor: theme.palette.border.main,
|
||||
},
|
||||
|
||||
flexButtons: {
|
||||
expDiv: {
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-evenly',
|
||||
},
|
||||
btnImg: {
|
||||
width: '0.8125rem',
|
||||
height: '0.8125rem',
|
||||
marginTop: theme.spacing(0.375),
|
||||
},
|
||||
btnText: {
|
||||
paddingLeft: theme.spacing(1.625),
|
||||
},
|
||||
deleteText: {
|
||||
color: theme.palette.error.main,
|
||||
},
|
||||
|
||||
buttonOutlineWarning: {
|
||||
borderColor: theme.palette.error.dark,
|
||||
// modal
|
||||
modalHeading: {
|
||||
fontSize: '1.5rem',
|
||||
lineHeight: '130%',
|
||||
fontWeight: 'bold',
|
||||
fontFeatureSettings: 'pnum on, lnum on',
|
||||
margin: theme.spacing(2.5, 0, 4.5),
|
||||
padding: theme.spacing(0, 6.5),
|
||||
},
|
||||
modalBodyText: {
|
||||
fontSize: '1rem',
|
||||
lineHeight: '130%',
|
||||
padding: theme.spacing(0, 6.5),
|
||||
},
|
||||
flexButtons: {
|
||||
display: 'flex',
|
||||
justifyContent: 'flex-end',
|
||||
padding: theme.spacing(5.5, 6.5, 0, 0),
|
||||
},
|
||||
|
||||
modal: {
|
||||
padding: theme.spacing(15, 0),
|
||||
padding: theme.spacing(5, 0),
|
||||
},
|
||||
buttonText: {
|
||||
lineHeight: '140%',
|
||||
fontSize: '0.875rem',
|
||||
},
|
||||
orText: {
|
||||
lineHeight: '140%',
|
||||
fontSize: '0.875rem',
|
||||
fontWeight: 500,
|
||||
padding: theme.spacing(0, 2),
|
||||
},
|
||||
disabledText: {
|
||||
color: theme.palette.text.disabled,
|
||||
},
|
||||
confirmButtonText: {
|
||||
color: theme.palette.text.secondary,
|
||||
padding: theme.spacing(0, 3),
|
||||
},
|
||||
cancelButton: {
|
||||
marginRight: theme.spacing(1.5),
|
||||
padding: theme.spacing(0, 3),
|
||||
},
|
||||
|
||||
// select
|
||||
menuList: {
|
||||
boxShadow: '0 5px 9px rgba(0, 0, 0, 0.1)',
|
||||
},
|
||||
menuListItem: {
|
||||
background: `${theme.palette.background.paper} !important`,
|
||||
fontSize: '0.875rem',
|
||||
lineHeight: '150%',
|
||||
height: '1.875rem',
|
||||
'&:hover': {
|
||||
background: `${theme.palette.cards.highlight} !important`,
|
||||
},
|
||||
'&.Mui-selected': {
|
||||
background: `${theme.palette.cards.highlight} !important`,
|
||||
},
|
||||
},
|
||||
}));
|
||||
|
||||
|
@ -239,12 +342,12 @@ export const useOutlinedInputStyles = makeStyles((theme: Theme) => ({
|
|||
borderColor: theme.palette.border.main,
|
||||
},
|
||||
'&:hover $notchedOutline': {
|
||||
borderColor: theme.palette.border.main,
|
||||
borderColor: theme.palette.highlight,
|
||||
},
|
||||
'&$focused $notchedOutline': {
|
||||
borderColor: theme.palette.border.main,
|
||||
borderColor: theme.palette.highlight,
|
||||
},
|
||||
height: '2.9rem',
|
||||
height: '2.75rem',
|
||||
},
|
||||
focused: {},
|
||||
notchedOutline: {},
|
||||
|
|
|
@ -1,12 +1,7 @@
|
|||
/* eslint-disable no-unused-expressions */
|
||||
import { useQuery } from '@apollo/client';
|
||||
import {
|
||||
FormControlLabel,
|
||||
FormGroup,
|
||||
Icon,
|
||||
Typography,
|
||||
} from '@material-ui/core';
|
||||
import { InputField, RadioButton } from 'litmus-ui';
|
||||
import { FormControlLabel, FormGroup, Typography } from '@material-ui/core';
|
||||
import { InputField, RadioButton, TextButton } from 'litmus-ui';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { LIST_DATASOURCE } from '../../../../graphql';
|
||||
|
@ -16,6 +11,8 @@ import {
|
|||
ListDataSourceResponse,
|
||||
ListDataSourceVars,
|
||||
} from '../../../../models/graphql/dataSourceDetails';
|
||||
import { ReactComponent as ExternalLinkIcon } from '../../../../svg/externalLink.svg';
|
||||
import { ReactComponent as DocsIcon } from '../../../../svg/prometheusDocs.svg';
|
||||
import { getProjectID } from '../../../../utils/getSearchParams';
|
||||
import {
|
||||
isValidWebUrl,
|
||||
|
@ -226,29 +223,22 @@ const ConfigurePrometheus: React.FC<ConfigurePrometheusProps> = ({
|
|||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className={classes.iconWithTextDiv}>
|
||||
<img
|
||||
src="/icons/docs.svg"
|
||||
alt="Docs icon"
|
||||
className={classes.inlineIcon}
|
||||
/>
|
||||
<TextButton
|
||||
className={classes.button}
|
||||
onClick={() =>
|
||||
window.open(
|
||||
'https://github.com/litmuschaos/litmus/tree/master/monitoring#model-1-optional-prometheus-scrape-config-model'
|
||||
)
|
||||
}
|
||||
startIcon={<DocsIcon className={classes.inlineIcon} />}
|
||||
endIcon={<ExternalLinkIcon className={classes.inlineIcon} />}
|
||||
classes={{ label: classes.buttonLabel }}
|
||||
variant="highlight"
|
||||
>
|
||||
<Typography className={classes.infoValue}>
|
||||
{t('analyticsDashboard.dataSourceForm.docsAndSetup')}
|
||||
</Typography>
|
||||
<Icon
|
||||
onClick={() => {
|
||||
window.open(
|
||||
'https://github.com/litmuschaos/litmus/tree/master/monitoring#model-1-optional-prometheus-scrape-config-model'
|
||||
);
|
||||
}}
|
||||
>
|
||||
<img
|
||||
src="/icons/externalLink.svg"
|
||||
alt="external link"
|
||||
className={classes.linkIcon}
|
||||
/>
|
||||
</Icon>
|
||||
</div>
|
||||
</TextButton>
|
||||
<div className={classes.horizontalLine} />
|
||||
<Typography className={classes.heading}>
|
||||
{t('analyticsDashboard.dataSourceForm.endPoint')}
|
||||
|
@ -271,7 +261,7 @@ const ConfigurePrometheus: React.FC<ConfigurePrometheusProps> = ({
|
|||
</div>
|
||||
<div className={classes.inputDiv}>
|
||||
<InputField
|
||||
label={t('analyticsDashboard.dataSourceForm.Access')}
|
||||
label={t('analyticsDashboard.dataSourceForm.access')}
|
||||
data-cy="inputDataSourceAccess"
|
||||
width="22.5rem"
|
||||
variant={
|
||||
|
|
|
@ -8,7 +8,7 @@ const useStyles = makeStyles((theme) => ({
|
|||
border: '1px',
|
||||
borderRadius: '3px',
|
||||
boxShadow:
|
||||
'0px 0.3px 0.9px rgba(0, 0, 0, 0.1), 0px 1.6px 3.6px rgba(0, 0, 0, 0.13)',
|
||||
'0 0.3px 0.9px rgba(0, 0, 0, 0.1), 0 1.6px 3.6px rgba(0, 0, 0, 0.13)',
|
||||
padding: theme.spacing(4, 3.125, 6),
|
||||
},
|
||||
|
||||
|
@ -63,20 +63,24 @@ const useStyles = makeStyles((theme) => ({
|
|||
},
|
||||
|
||||
inlineIcon: {
|
||||
margin: theme.spacing(0.5, 1, 0, 0),
|
||||
width: '1rem',
|
||||
height: '1rem',
|
||||
margin: theme.spacing(0.25, 0),
|
||||
width: '1.25rem',
|
||||
height: '1.25rem',
|
||||
},
|
||||
|
||||
linkIcon: {
|
||||
margin: theme.spacing(0, 0, 0.45, 0.75),
|
||||
width: '1rem',
|
||||
height: '1rem',
|
||||
},
|
||||
|
||||
iconWithTextDiv: {
|
||||
display: 'flex',
|
||||
button: {
|
||||
minWidth: 0,
|
||||
minHeight: 0,
|
||||
padding: 0,
|
||||
margin: theme.spacing(1.5, 0, 0, 2),
|
||||
'&:hover': {
|
||||
cursor: 'pointer !important',
|
||||
},
|
||||
},
|
||||
|
||||
buttonLabel: {
|
||||
justifyContent: 'flex-start',
|
||||
marginLeft: theme.spacing(0.5),
|
||||
},
|
||||
|
||||
infoValue: {
|
||||
|
|
|
@ -1,21 +1,19 @@
|
|||
import { ApolloError, useMutation } from '@apollo/client';
|
||||
import { Typography } from '@material-ui/core';
|
||||
import { ButtonFilled, ButtonOutlined, LightPills, Modal } from 'litmus-ui';
|
||||
import { IconButton, Menu, MenuItem, Typography } from '@material-ui/core';
|
||||
import MoreVertIcon from '@material-ui/icons/MoreVert';
|
||||
import { ButtonFilled, LightPills, Modal, TextButton } from 'litmus-ui';
|
||||
import moment from 'moment';
|
||||
import React, { useEffect } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { DELETE_DATASOURCE } from '../../../../graphql';
|
||||
import {
|
||||
DeleteDataSourceInput,
|
||||
deleteDSInput,
|
||||
ListDataSourceResponse,
|
||||
} from '../../../../models/graphql/dataSourceDetails';
|
||||
import useActions from '../../../../redux/actions';
|
||||
import * as DataSourceActions from '../../../../redux/actions/dataSource';
|
||||
import * as TabActions from '../../../../redux/actions/tabs';
|
||||
import { history } from '../../../../redux/configureStore';
|
||||
import { ReactComponent as CogWheelIcon } from '../../../../svg/cogwheel.svg';
|
||||
import { ReactComponent as CrossMarkIcon } from '../../../../svg/crossmark.svg';
|
||||
import { ReactComponent as BinIcon } from '../../../../svg/delete.svg';
|
||||
import { ReactComponent as ExternalLinkIcon } from '../../../../svg/externalLink.svg';
|
||||
import {
|
||||
getProjectID,
|
||||
getProjectRole,
|
||||
|
@ -24,45 +22,59 @@ import useStyles, { StyledTableCell } from './styles';
|
|||
|
||||
interface TableDataProps {
|
||||
data: ListDataSourceResponse;
|
||||
drawerStateHandler: (
|
||||
ds_id: string,
|
||||
ds_name: string,
|
||||
dashboards: string[]
|
||||
) => void;
|
||||
alertStateHandler: (successState: boolean) => void;
|
||||
}
|
||||
|
||||
const TableData: React.FC<TableDataProps> = ({ data }) => {
|
||||
const TableData: React.FC<TableDataProps> = ({
|
||||
data,
|
||||
drawerStateHandler,
|
||||
alertStateHandler,
|
||||
}) => {
|
||||
const classes = useStyles();
|
||||
const { t } = useTranslation();
|
||||
const projectID = getProjectID();
|
||||
const projectRole = getProjectRole();
|
||||
const dataSource = useActions(DataSourceActions);
|
||||
const tabs = useActions(TabActions);
|
||||
const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null);
|
||||
const open = Boolean(anchorEl);
|
||||
const [mutate, setMutate] = React.useState(false);
|
||||
const [confirm, setConfirm] = React.useState(false);
|
||||
const [success, setSuccess] = React.useState(false);
|
||||
const [connectedDashboardsMessage, setConnectedDashboardsMessage] =
|
||||
React.useState<string>('');
|
||||
const [open, setOpen] = React.useState(false);
|
||||
const [dataSourceSelectedForDeleting, setDataSourceSelectedForDeleting] =
|
||||
React.useState<deleteDSInput>({
|
||||
ds_id: '',
|
||||
force_delete: false,
|
||||
});
|
||||
const [openModal, setOpenModal] = React.useState(false);
|
||||
|
||||
// Function to convert UNIX time in format of dddd, DD MMM YYYY, HH:mm
|
||||
const formatDate = (date: string) => {
|
||||
const updated = new Date(parseInt(date, 10) * 1000).toString();
|
||||
const resDate = moment(updated).format('dddd, DD MMM YYYY, HH:mm');
|
||||
return resDate;
|
||||
};
|
||||
const handleClick = (event: React.MouseEvent<HTMLElement>) => {
|
||||
setAnchorEl(event.currentTarget);
|
||||
};
|
||||
const handleClose = () => {
|
||||
setAnchorEl(null);
|
||||
};
|
||||
|
||||
const [deleteDataSource] = useMutation<boolean, DeleteDataSourceInput>(
|
||||
DELETE_DATASOURCE,
|
||||
{
|
||||
onCompleted: () => {
|
||||
setConnectedDashboardsMessage('');
|
||||
setSuccess(true);
|
||||
setMutate(false);
|
||||
setOpen(true);
|
||||
alertStateHandler(true);
|
||||
},
|
||||
onError: (error: ApolloError) => {
|
||||
setConnectedDashboardsMessage(error.message);
|
||||
setMutate(false);
|
||||
setOpen(true);
|
||||
alertStateHandler(false);
|
||||
if (error.message.includes('dashboard(s)')) {
|
||||
const dashboardList: string = error.message.split(':')[1].trim();
|
||||
const dashboardNames: string[] = dashboardList
|
||||
.substring(1, dashboardList.length - 1)
|
||||
.split(',');
|
||||
drawerStateHandler(data.ds_id, data.ds_name, dashboardNames);
|
||||
}
|
||||
},
|
||||
}
|
||||
);
|
||||
|
@ -70,7 +82,12 @@ const TableData: React.FC<TableDataProps> = ({ data }) => {
|
|||
useEffect(() => {
|
||||
if (mutate === true) {
|
||||
deleteDataSource({
|
||||
variables: { deleteDSInput: dataSourceSelectedForDeleting },
|
||||
variables: {
|
||||
deleteDSInput: {
|
||||
ds_id: data.ds_id,
|
||||
force_delete: false,
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
}, [mutate]);
|
||||
|
@ -86,240 +103,177 @@ const TableData: React.FC<TableDataProps> = ({ data }) => {
|
|||
<LightPills variant="danger" label={data.health_status} />
|
||||
)}
|
||||
</StyledTableCell>
|
||||
<StyledTableCell className={classes.dataSourceName}>
|
||||
<Typography variant="body2">
|
||||
<strong>{data.ds_name}</strong>
|
||||
<StyledTableCell className={classes.columnDivider}>
|
||||
<Typography
|
||||
className={`${classes.tableObjects} ${classes.dataSourceNameColData}`}
|
||||
>
|
||||
{data.ds_name}
|
||||
</Typography>
|
||||
</StyledTableCell>
|
||||
<StyledTableCell className={classes.dataSourceType}>
|
||||
<Typography variant="body2">
|
||||
<strong>{data.ds_type}</strong>
|
||||
<StyledTableCell className={classes.dividerPadding}>
|
||||
<Typography
|
||||
className={classes.tableObjects}
|
||||
style={{ maxWidth: '5rem' }}
|
||||
>
|
||||
<img
|
||||
src="/icons/prometheus.svg"
|
||||
alt="Prometheus"
|
||||
className={classes.inlineIcon}
|
||||
/>
|
||||
{data.ds_type}
|
||||
</Typography>
|
||||
</StyledTableCell>
|
||||
<StyledTableCell className={classes.dataSourceName}>
|
||||
<Typography variant="body2">{formatDate(data.updated_at)}</Typography>
|
||||
</StyledTableCell>
|
||||
<StyledTableCell
|
||||
onClick={() => {
|
||||
dataSource.selectDataSource({
|
||||
selectedDataSourceID: data.ds_id,
|
||||
selectedDataSourceName: data.ds_name,
|
||||
});
|
||||
history.push({
|
||||
pathname: '/analytics/datasource/configure',
|
||||
search: `?projectID=${projectID}&projectRole=${projectRole}`,
|
||||
});
|
||||
}}
|
||||
>
|
||||
<Typography variant="body2" align="center">
|
||||
<CogWheelIcon className={classes.cogWheelIcon} />
|
||||
</Typography>
|
||||
<Typography variant="body2" align="center">
|
||||
Configure
|
||||
<StyledTableCell>
|
||||
<Typography
|
||||
className={classes.tableObjects}
|
||||
style={{ maxWidth: '12.5rem' }}
|
||||
>
|
||||
<img src="/icons/calendarIcon.svg" alt="Calender" />
|
||||
{formatDate(data.updated_at)}
|
||||
</Typography>
|
||||
</StyledTableCell>
|
||||
<StyledTableCell
|
||||
onClick={() => {
|
||||
const deleteDataSourceInput: deleteDSInput = {
|
||||
force_delete: false,
|
||||
ds_id: data.ds_id,
|
||||
};
|
||||
setDataSourceSelectedForDeleting(deleteDataSourceInput);
|
||||
setOpen(true);
|
||||
}}
|
||||
>
|
||||
<Typography variant="body2" align="center">
|
||||
<BinIcon className={classes.binIcon} />
|
||||
</Typography>
|
||||
<Typography variant="body2" align="center" className={classes.delete}>
|
||||
Delete
|
||||
</Typography>
|
||||
|
||||
<StyledTableCell>
|
||||
<TextButton
|
||||
className={classes.button}
|
||||
onClick={() => window.open(data.ds_url)}
|
||||
endIcon={<ExternalLinkIcon className={classes.inlineIcon} />}
|
||||
classes={{ label: classes.buttonLabel }}
|
||||
>
|
||||
<Typography
|
||||
className={`${classes.tableObjects} ${classes.dataSourceUrlColData}`}
|
||||
>
|
||||
{data.ds_url}
|
||||
</Typography>
|
||||
</TextButton>
|
||||
</StyledTableCell>
|
||||
|
||||
<StyledTableCell>
|
||||
<IconButton
|
||||
aria-label="more"
|
||||
aria-controls="long-menu"
|
||||
aria-haspopup="true"
|
||||
onClick={handleClick}
|
||||
data-cy="browseDataSourceOptions"
|
||||
>
|
||||
<MoreVertIcon className={classes.headerIcon} />
|
||||
</IconButton>
|
||||
<Menu
|
||||
id="long-menu"
|
||||
anchorEl={anchorEl}
|
||||
keepMounted
|
||||
open={open}
|
||||
onClose={handleClose}
|
||||
anchorOrigin={{
|
||||
vertical: 'bottom',
|
||||
horizontal: 'right',
|
||||
}}
|
||||
transformOrigin={{
|
||||
vertical: 'top',
|
||||
horizontal: 'right',
|
||||
}}
|
||||
getContentAnchorEl={null}
|
||||
classes={{ paper: classes.menuList }}
|
||||
>
|
||||
<MenuItem
|
||||
value="Configure"
|
||||
onClick={() => {
|
||||
dataSource.selectDataSource({
|
||||
selectedDataSourceID: data.ds_id,
|
||||
selectedDataSourceName: data.ds_name,
|
||||
});
|
||||
history.push({
|
||||
pathname: '/analytics/datasource/configure',
|
||||
search: `?projectID=${projectID}&projectRole=${projectRole}`,
|
||||
});
|
||||
}}
|
||||
className={classes.menuItem}
|
||||
>
|
||||
<div className={classes.expDiv}>
|
||||
<img
|
||||
src="/icons/cogwheel.svg"
|
||||
alt="Configure"
|
||||
className={classes.btnImg}
|
||||
/>
|
||||
<Typography
|
||||
data-cy="configureDashboard"
|
||||
className={classes.btnText}
|
||||
>
|
||||
{t('analyticsDashboard.dataSourceTable.configure')}
|
||||
</Typography>
|
||||
</div>
|
||||
</MenuItem>
|
||||
|
||||
<MenuItem
|
||||
value="Delete"
|
||||
onClick={() => {
|
||||
setOpenModal(true);
|
||||
handleClose();
|
||||
}}
|
||||
className={classes.menuItem}
|
||||
>
|
||||
<div className={classes.expDiv}>
|
||||
<img
|
||||
src="/icons/delete.svg"
|
||||
alt="Delete"
|
||||
className={classes.btnImg}
|
||||
/>
|
||||
<Typography
|
||||
data-cy="deleteDashboard"
|
||||
className={`${classes.btnText} ${classes.deleteText}`}
|
||||
>
|
||||
{t('analyticsDashboard.dataSourceTable.delete')}
|
||||
</Typography>
|
||||
</div>
|
||||
</MenuItem>
|
||||
</Menu>
|
||||
</StyledTableCell>
|
||||
<Modal
|
||||
open={open}
|
||||
onClose={() => {
|
||||
setOpen(false);
|
||||
setConfirm(false);
|
||||
}}
|
||||
width="60%"
|
||||
modalActions={
|
||||
<ButtonOutlined
|
||||
className={classes.closeButton}
|
||||
onClick={() => {
|
||||
setOpen(false);
|
||||
setConfirm(false);
|
||||
}}
|
||||
>
|
||||
✕
|
||||
</ButtonOutlined>
|
||||
}
|
||||
open={openModal}
|
||||
onClose={() => setOpenModal(false)}
|
||||
width="45%"
|
||||
height="fit-content"
|
||||
>
|
||||
<div className={classes.modal}>
|
||||
{confirm === true ? (
|
||||
<Typography align="center">
|
||||
{success === true ? (
|
||||
<img
|
||||
src="/icons/finish.svg"
|
||||
alt="success"
|
||||
className={classes.icon}
|
||||
/>
|
||||
) : (
|
||||
<CrossMarkIcon className={classes.icon} />
|
||||
)}
|
||||
</Typography>
|
||||
) : (
|
||||
<div />
|
||||
)}
|
||||
<Typography className={classes.modalHeading} align="left">
|
||||
{t('analyticsDashboard.dataSourceTable.modal.removeDataSource')}
|
||||
</Typography>
|
||||
|
||||
{confirm === true ? (
|
||||
<Typography
|
||||
className={classes.modalHeading}
|
||||
align="center"
|
||||
variant="h3"
|
||||
<Typography className={classes.modalBodyText} align="left">
|
||||
{t(
|
||||
'analyticsDashboard.dataSourceTable.modal.removeDataSourceConfirmation'
|
||||
)}
|
||||
<b>
|
||||
<i>{` ${data.ds_name} `}</i>
|
||||
</b>
|
||||
?
|
||||
</Typography>
|
||||
|
||||
<div
|
||||
className={`${classes.flexButtons} ${classes.flexButtonsPadding}`}
|
||||
>
|
||||
<TextButton
|
||||
onClick={() => setOpenModal(false)}
|
||||
className={classes.cancelButton}
|
||||
>
|
||||
{success === true
|
||||
? `The data source is successfully deleted.`
|
||||
: success === false &&
|
||||
dataSourceSelectedForDeleting?.force_delete === false
|
||||
? `${connectedDashboardsMessage}`
|
||||
: `There was a problem deleting your data source.`}
|
||||
</Typography>
|
||||
) : (
|
||||
<div />
|
||||
)}
|
||||
|
||||
{confirm === true ? (
|
||||
<Typography
|
||||
align="center"
|
||||
variant="body1"
|
||||
className={classes.modalBody}
|
||||
>
|
||||
{success === true ? (
|
||||
<div>
|
||||
You will see the data source deleted in the datasource table.
|
||||
</div>
|
||||
) : success === false &&
|
||||
dataSourceSelectedForDeleting?.force_delete === false ? (
|
||||
<div>
|
||||
Dashboard(s) configured to use this data source will be
|
||||
deleted.
|
||||
</div>
|
||||
) : (
|
||||
<div>
|
||||
Error encountered while deleting the data source. Please try
|
||||
again.
|
||||
</div>
|
||||
)}
|
||||
</Typography>
|
||||
) : (
|
||||
<div />
|
||||
)}
|
||||
|
||||
{success === true && confirm === true ? (
|
||||
<Typography className={classes.buttonText}>
|
||||
{t('analyticsDashboard.dataSourceTable.modal.cancel')}
|
||||
</Typography>
|
||||
</TextButton>
|
||||
<ButtonFilled
|
||||
variant="success"
|
||||
onClick={() => {
|
||||
setConfirm(false);
|
||||
setOpen(false);
|
||||
tabs.changeAnalyticsDashboardTabs(3);
|
||||
window.location.reload();
|
||||
setMutate(true);
|
||||
setOpenModal(false);
|
||||
}}
|
||||
variant="error"
|
||||
>
|
||||
<div>Back to Data Source</div>
|
||||
<Typography
|
||||
className={`${classes.buttonText} ${classes.confirmButtonText}`}
|
||||
>
|
||||
{t('analyticsDashboard.dataSourceTable.modal.delete')}
|
||||
</Typography>
|
||||
</ButtonFilled>
|
||||
) : success === false && confirm === true ? (
|
||||
<div className={classes.flexButtons}>
|
||||
{dataSourceSelectedForDeleting?.force_delete === false ? (
|
||||
<ButtonFilled
|
||||
variant="error"
|
||||
onClick={() => {
|
||||
setDataSourceSelectedForDeleting({
|
||||
ds_id: dataSourceSelectedForDeleting.ds_id,
|
||||
force_delete: true,
|
||||
});
|
||||
setOpen(false);
|
||||
setMutate(true);
|
||||
}}
|
||||
>
|
||||
<div>Force Delete</div>
|
||||
</ButtonFilled>
|
||||
) : (
|
||||
<ButtonOutlined
|
||||
className={classes.buttonOutlineWarning}
|
||||
onClick={() => {
|
||||
setOpen(false);
|
||||
setMutate(true);
|
||||
}}
|
||||
disabled={false}
|
||||
>
|
||||
<div>Try Again</div>
|
||||
</ButtonOutlined>
|
||||
)}
|
||||
|
||||
<ButtonFilled
|
||||
onClick={() => {
|
||||
setConfirm(false);
|
||||
setOpen(false);
|
||||
tabs.changeAnalyticsDashboardTabs(3);
|
||||
window.location.reload();
|
||||
}}
|
||||
>
|
||||
<div>Back to Data Source</div>
|
||||
</ButtonFilled>
|
||||
</div>
|
||||
) : (
|
||||
<div>
|
||||
<Typography align="center">
|
||||
<img
|
||||
src="/icons/delete_large_icon.svg"
|
||||
alt="delete"
|
||||
className={classes.icon}
|
||||
/>
|
||||
</Typography>
|
||||
|
||||
<Typography
|
||||
className={classes.modalHeading}
|
||||
align="center"
|
||||
variant="h3"
|
||||
>
|
||||
Are you sure to remove this data source?
|
||||
</Typography>
|
||||
|
||||
<Typography
|
||||
align="center"
|
||||
variant="body1"
|
||||
className={classes.modalBody}
|
||||
>
|
||||
The following action cannot be reverted.
|
||||
</Typography>
|
||||
|
||||
<div className={classes.flexButtons}>
|
||||
<ButtonOutlined
|
||||
onClick={() => {
|
||||
setOpen(false);
|
||||
history.push({
|
||||
pathname: '/analytics',
|
||||
search: `?projectID=${projectID}&projectRole=${projectRole}`,
|
||||
});
|
||||
}}
|
||||
disabled={false}
|
||||
>
|
||||
<div>No</div>
|
||||
</ButtonOutlined>
|
||||
|
||||
<ButtonFilled
|
||||
variant="error"
|
||||
onClick={() => {
|
||||
setConfirm(true);
|
||||
setOpen(false);
|
||||
setMutate(true);
|
||||
}}
|
||||
>
|
||||
<div>Yes</div>
|
||||
</ButtonFilled>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</Modal>
|
||||
</>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { IconButton, TableHead, TableRow } from '@material-ui/core';
|
||||
import { IconButton, TableHead, TableRow, Typography } from '@material-ui/core';
|
||||
import ExpandLessTwoToneIcon from '@material-ui/icons/ExpandLessTwoTone';
|
||||
import ExpandMoreTwoToneIcon from '@material-ui/icons/ExpandMoreTwoTone';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
|
@ -8,8 +8,6 @@ import useStyles, { StyledTableCell } from './styles';
|
|||
interface SortData {
|
||||
lastConfigured: { sort: boolean; ascending: boolean };
|
||||
name: { sort: boolean; ascending: boolean };
|
||||
status: { sort: boolean; ascending: boolean };
|
||||
dataSourceType: { sort: boolean; ascending: boolean };
|
||||
}
|
||||
|
||||
interface SortCallBackType {
|
||||
|
@ -27,8 +25,6 @@ const TableHeader: React.FC<TableHeaderProps> = ({ callBackToSort }) => {
|
|||
const [sortData, setSortData] = useState<SortData>({
|
||||
name: { sort: false, ascending: true },
|
||||
lastConfigured: { sort: true, ascending: false },
|
||||
status: { sort: false, ascending: true },
|
||||
dataSourceType: { sort: false, ascending: true },
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
|
@ -37,52 +33,22 @@ const TableHeader: React.FC<TableHeaderProps> = ({ callBackToSort }) => {
|
|||
|
||||
return (
|
||||
<TableHead>
|
||||
<TableRow className={classes.tableHead}>
|
||||
<TableRow>
|
||||
<StyledTableCell className={classes.headSpacing}>
|
||||
<div className={classes.nameContent}>
|
||||
<div className={classes.dataSourceStatusHead}>
|
||||
<b>{t('analyticsDashboard.dataSourceTable.tableHead1')}</b>
|
||||
</div>
|
||||
<div className={classes.nameContentIcons}>
|
||||
<IconButton
|
||||
aria-label="sort status ascending"
|
||||
size="small"
|
||||
onClick={() =>
|
||||
setSortData({
|
||||
...sortData,
|
||||
name: { sort: false, ascending: true },
|
||||
lastConfigured: { sort: false, ascending: true },
|
||||
status: { sort: true, ascending: true },
|
||||
dataSourceType: { sort: false, ascending: true },
|
||||
})
|
||||
}
|
||||
>
|
||||
<ExpandLessTwoToneIcon className={classes.markerIconUp} />
|
||||
</IconButton>
|
||||
<IconButton
|
||||
aria-label="sort status descending"
|
||||
size="small"
|
||||
onClick={() =>
|
||||
setSortData({
|
||||
...sortData,
|
||||
name: { sort: false, ascending: true },
|
||||
lastConfigured: { sort: false, ascending: true },
|
||||
status: { sort: true, ascending: false },
|
||||
dataSourceType: { sort: false, ascending: true },
|
||||
})
|
||||
}
|
||||
>
|
||||
<ExpandMoreTwoToneIcon className={classes.markerIconDown} />
|
||||
</IconButton>
|
||||
</div>
|
||||
</div>
|
||||
<Typography
|
||||
className={`${classes.dataSourceNameHead} ${classes.dataSourceStatusHeadWithoutSort}`}
|
||||
>
|
||||
{t('analyticsDashboard.dataSourceTable.tableHead1')}
|
||||
</Typography>
|
||||
</StyledTableCell>
|
||||
|
||||
<StyledTableCell className={classes.dataSourceName}>
|
||||
<StyledTableCell
|
||||
className={`${classes.headSpacing} ${classes.columnDivider}`}
|
||||
>
|
||||
<div className={classes.nameContent}>
|
||||
<div className={classes.dataSourceNameHead}>
|
||||
<b>{t('analyticsDashboard.dataSourceTable.tableHead2')}</b>
|
||||
</div>
|
||||
<Typography className={classes.dataSourceNameHead}>
|
||||
{t('analyticsDashboard.dataSourceTable.tableHead2')}
|
||||
</Typography>
|
||||
<div className={classes.nameContentIcons}>
|
||||
<IconButton
|
||||
aria-label="sort name ascending"
|
||||
|
@ -92,12 +58,10 @@ const TableHeader: React.FC<TableHeaderProps> = ({ callBackToSort }) => {
|
|||
...sortData,
|
||||
name: { sort: true, ascending: true },
|
||||
lastConfigured: { sort: false, ascending: true },
|
||||
status: { sort: false, ascending: true },
|
||||
dataSourceType: { sort: false, ascending: true },
|
||||
})
|
||||
}
|
||||
>
|
||||
<ExpandLessTwoToneIcon className={classes.markerIconUp} />
|
||||
<ExpandLessTwoToneIcon className={classes.markerIcon} />
|
||||
</IconButton>
|
||||
<IconButton
|
||||
aria-label="sort name descending"
|
||||
|
@ -107,62 +71,38 @@ const TableHeader: React.FC<TableHeaderProps> = ({ callBackToSort }) => {
|
|||
...sortData,
|
||||
name: { sort: true, ascending: false },
|
||||
lastConfigured: { sort: false, ascending: true },
|
||||
status: { sort: false, ascending: true },
|
||||
dataSourceType: { sort: false, ascending: true },
|
||||
})
|
||||
}
|
||||
>
|
||||
<ExpandMoreTwoToneIcon className={classes.markerIconDown} />
|
||||
<ExpandMoreTwoToneIcon className={classes.markerIcon} />
|
||||
</IconButton>
|
||||
</div>
|
||||
</div>
|
||||
</StyledTableCell>
|
||||
|
||||
<StyledTableCell
|
||||
className={`${classes.headSpacing} ${classes.dividerPadding}`}
|
||||
>
|
||||
<Typography
|
||||
className={`${classes.dataSourceNameHead} ${classes.dataSourceNameHeadWithoutSort}`}
|
||||
>
|
||||
{t('analyticsDashboard.dataSourceTable.tableHead3')}
|
||||
</Typography>
|
||||
</StyledTableCell>
|
||||
|
||||
<StyledTableCell className={classes.headSpacing}>
|
||||
<Typography
|
||||
className={`${classes.dataSourceNameHead} ${classes.dataSourceNameHeadWithoutSort}`}
|
||||
>
|
||||
{t('analyticsDashboard.dataSourceTable.tableHead4')}
|
||||
</Typography>
|
||||
</StyledTableCell>
|
||||
|
||||
<StyledTableCell className={classes.headSpacing}>
|
||||
<div className={classes.nameContent}>
|
||||
<div className={classes.dataSourceNameHead}>
|
||||
<b>{t('analyticsDashboard.dataSourceTable.tableHead3')}</b>
|
||||
</div>
|
||||
<div className={classes.nameContentIcons}>
|
||||
<IconButton
|
||||
aria-label="sort datasource type ascending"
|
||||
size="small"
|
||||
onClick={() =>
|
||||
setSortData({
|
||||
...sortData,
|
||||
name: { sort: false, ascending: true },
|
||||
lastConfigured: { sort: false, ascending: true },
|
||||
status: { sort: false, ascending: true },
|
||||
dataSourceType: { sort: true, ascending: true },
|
||||
})
|
||||
}
|
||||
>
|
||||
<ExpandLessTwoToneIcon className={classes.markerIconUp} />
|
||||
</IconButton>
|
||||
<IconButton
|
||||
aria-label="sort datasource type descending"
|
||||
size="small"
|
||||
onClick={() =>
|
||||
setSortData({
|
||||
...sortData,
|
||||
name: { sort: false, ascending: true },
|
||||
lastConfigured: { sort: false, ascending: true },
|
||||
status: { sort: false, ascending: true },
|
||||
dataSourceType: { sort: true, ascending: false },
|
||||
})
|
||||
}
|
||||
>
|
||||
<ExpandMoreTwoToneIcon className={classes.markerIconDown} />
|
||||
</IconButton>
|
||||
</div>
|
||||
</div>
|
||||
</StyledTableCell>
|
||||
|
||||
<StyledTableCell className={classes.dataSourceName}>
|
||||
<div className={classes.nameContent}>
|
||||
<div className={classes.dataSourceNameHead}>
|
||||
<b>{t('analyticsDashboard.dataSourceTable.tableHead4')}</b>
|
||||
</div>
|
||||
<Typography className={classes.dataSourceNameHead}>
|
||||
{t('analyticsDashboard.dataSourceTable.tableHead5')}
|
||||
</Typography>
|
||||
<div className={classes.nameContentIcons}>
|
||||
<IconButton
|
||||
aria-label="sort last configured ascending"
|
||||
|
@ -172,12 +112,10 @@ const TableHeader: React.FC<TableHeaderProps> = ({ callBackToSort }) => {
|
|||
...sortData,
|
||||
name: { sort: false, ascending: true },
|
||||
lastConfigured: { sort: true, ascending: true },
|
||||
status: { sort: false, ascending: true },
|
||||
dataSourceType: { sort: false, ascending: true },
|
||||
})
|
||||
}
|
||||
>
|
||||
<ExpandLessTwoToneIcon className={classes.markerIconUp} />
|
||||
<ExpandLessTwoToneIcon className={classes.markerIcon} />
|
||||
</IconButton>
|
||||
<IconButton
|
||||
aria-label="sort last configured descending"
|
||||
|
@ -187,18 +125,15 @@ const TableHeader: React.FC<TableHeaderProps> = ({ callBackToSort }) => {
|
|||
...sortData,
|
||||
name: { sort: false, ascending: true },
|
||||
lastConfigured: { sort: true, ascending: false },
|
||||
status: { sort: false, ascending: true },
|
||||
dataSourceType: { sort: false, ascending: true },
|
||||
})
|
||||
}
|
||||
>
|
||||
<ExpandMoreTwoToneIcon className={classes.markerIconDown} />
|
||||
<ExpandMoreTwoToneIcon className={classes.markerIcon} />
|
||||
</IconButton>
|
||||
</div>
|
||||
</div>
|
||||
</StyledTableCell>
|
||||
<StyledTableCell className={classes.options} />
|
||||
<StyledTableCell className={classes.options} />
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
);
|
||||
|
|
|
@ -2,18 +2,15 @@ import {
|
|||
Button,
|
||||
FormControl,
|
||||
IconButton,
|
||||
InputAdornment,
|
||||
InputBase,
|
||||
InputLabel,
|
||||
MenuItem,
|
||||
OutlinedInput,
|
||||
Select,
|
||||
Typography,
|
||||
} from '@material-ui/core';
|
||||
import ChevronRightIcon from '@material-ui/icons/ChevronRight';
|
||||
import KeyboardArrowDownIcon from '@material-ui/icons/KeyboardArrowDown';
|
||||
import SearchIcon from '@material-ui/icons/Search';
|
||||
import { ButtonFilled } from 'litmus-ui';
|
||||
import KeyboardArrowUpIcon from '@material-ui/icons/KeyboardArrowUp';
|
||||
import { ButtonFilled, Search } from 'litmus-ui';
|
||||
import React, { ChangeEvent, useRef, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import DateRangeSelector from '../../../../components/DateRangeSelector';
|
||||
|
@ -107,121 +104,151 @@ const TableToolBar: React.FC<TableToolBarProps> = ({
|
|||
|
||||
return (
|
||||
<div className={classes.headerSection}>
|
||||
<InputBase
|
||||
id="input-with-icon-adornment"
|
||||
placeholder="Search"
|
||||
className={classes.search}
|
||||
value={searchToken}
|
||||
onChange={handleSearch}
|
||||
classes={{
|
||||
input: classes.input,
|
||||
}}
|
||||
startAdornment={
|
||||
<InputAdornment position="start">
|
||||
<SearchIcon />
|
||||
</InputAdornment>
|
||||
}
|
||||
/>
|
||||
|
||||
<FormControl
|
||||
variant="outlined"
|
||||
className={`${classes.formControl} ${classes.dataSourceStatusForm}`}
|
||||
<div className={classes.search}>
|
||||
<Search
|
||||
id="input-with-icon-textfield"
|
||||
placeholder={t('analyticsDashboard.dataSourceTable.search')}
|
||||
value={searchToken}
|
||||
onChange={handleSearch}
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
className={classes.headerSection}
|
||||
style={{ justifyContent: 'flex-end' }}
|
||||
>
|
||||
<InputLabel className={classes.selectText}> Status </InputLabel>
|
||||
<Select
|
||||
label="Status"
|
||||
value={status}
|
||||
onChange={handleStatusChange}
|
||||
className={classes.selectText}
|
||||
input={<OutlinedInput classes={outlinedInputClasses} />}
|
||||
<FormControl
|
||||
variant="outlined"
|
||||
className={`${classes.formControl} ${classes.dataSourceStatusForm}`}
|
||||
>
|
||||
<MenuItem value="All">All</MenuItem>
|
||||
{statuses.map((status: string) => (
|
||||
<MenuItem
|
||||
key={`${status}-analyticsDashboard-dataSource-toolbar`}
|
||||
value={status}
|
||||
>
|
||||
{status}
|
||||
<InputLabel className={classes.selectText}>
|
||||
{t('analyticsDashboard.dataSourceTable.tableHead1')}
|
||||
</InputLabel>
|
||||
<Select
|
||||
label={t('analyticsDashboard.dataSourceTable.tableHead1')}
|
||||
value={status}
|
||||
onChange={handleStatusChange}
|
||||
className={classes.selectText}
|
||||
input={<OutlinedInput classes={outlinedInputClasses} />}
|
||||
IconComponent={KeyboardArrowDownIcon}
|
||||
MenuProps={{
|
||||
anchorOrigin: {
|
||||
vertical: 'bottom',
|
||||
horizontal: 'right',
|
||||
},
|
||||
transformOrigin: {
|
||||
vertical: 'top',
|
||||
horizontal: 'right',
|
||||
},
|
||||
getContentAnchorEl: null,
|
||||
classes: { paper: classes.menuList },
|
||||
}}
|
||||
>
|
||||
<MenuItem value="All" className={classes.menuListItem}>
|
||||
{t('analyticsDashboard.dataSourceTable.all')}
|
||||
</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
</FormControl>
|
||||
{statuses.map((availableStatus: string) => (
|
||||
<MenuItem
|
||||
key={`${availableStatus}-analyticsDashboard-dataSource-toolbar`}
|
||||
value={availableStatus}
|
||||
className={classes.menuListItem}
|
||||
>
|
||||
{availableStatus}
|
||||
</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
</FormControl>
|
||||
|
||||
<FormControl
|
||||
variant="outlined"
|
||||
className={`${classes.formControl} ${classes.dataSourceNameForm}`}
|
||||
>
|
||||
<InputLabel className={classes.selectText}>
|
||||
{' '}
|
||||
Data Source Type{' '}
|
||||
</InputLabel>
|
||||
<Select
|
||||
label="Data Source Type"
|
||||
value={dataSourceType}
|
||||
onChange={handleDataSourceTypeChange}
|
||||
className={classes.selectText}
|
||||
input={<OutlinedInput classes={outlinedInputClasses} />}
|
||||
<FormControl
|
||||
variant="outlined"
|
||||
className={`${classes.formControl} ${classes.dataSourceNameForm}`}
|
||||
>
|
||||
<MenuItem value="All">All</MenuItem>
|
||||
{dataSourceTypes.map((dataSourceType: string) => (
|
||||
<MenuItem
|
||||
key={`${dataSourceType}-analyticsDashboard-dataSource-tableToolbar`}
|
||||
value={dataSourceType}
|
||||
>
|
||||
{dataSourceType}
|
||||
<InputLabel className={classes.selectText}>
|
||||
{t('analyticsDashboard.dataSourceTable.tableHead3')}
|
||||
</InputLabel>
|
||||
<Select
|
||||
label={t('analyticsDashboard.dataSourceTable.tableHead3')}
|
||||
value={dataSourceType}
|
||||
onChange={handleDataSourceTypeChange}
|
||||
className={classes.selectText}
|
||||
input={<OutlinedInput classes={outlinedInputClasses} />}
|
||||
IconComponent={KeyboardArrowDownIcon}
|
||||
MenuProps={{
|
||||
anchorOrigin: {
|
||||
vertical: 'bottom',
|
||||
horizontal: 'right',
|
||||
},
|
||||
transformOrigin: {
|
||||
vertical: 'top',
|
||||
horizontal: 'right',
|
||||
},
|
||||
getContentAnchorEl: null,
|
||||
classes: { paper: classes.menuList },
|
||||
}}
|
||||
>
|
||||
<MenuItem value="All" className={classes.menuListItem}>
|
||||
{t('analyticsDashboard.dataSourceTable.all')}
|
||||
</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
</FormControl>
|
||||
{dataSourceTypes.map((availableDataSourceType: string) => (
|
||||
<MenuItem
|
||||
key={`${availableDataSourceType}-analyticsDashboard-dataSource-tableToolbar`}
|
||||
value={availableDataSourceType}
|
||||
className={classes.menuListItem}
|
||||
>
|
||||
{availableDataSourceType}
|
||||
</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
</FormControl>
|
||||
|
||||
<Button
|
||||
className={classes.selectDate}
|
||||
onClick={() => setDateRangeSelectorPopoverOpen(true)}
|
||||
ref={dateRangeSelectorRef}
|
||||
aria-label="time range"
|
||||
aria-haspopup="true"
|
||||
>
|
||||
<Typography className={classes.displayDate}>
|
||||
{range.startDate === ' '
|
||||
? 'Select Period'
|
||||
: `${range.startDate.split(' ')[2]} ${
|
||||
range.startDate.split(' ')[1]
|
||||
} ${range.startDate.split(' ')[3]} - ${
|
||||
range.endDate.split(' ')[2]
|
||||
} ${range.endDate.split(' ')[1]} ${range.endDate.split(' ')[3]}`}
|
||||
<Button
|
||||
className={`${classes.selectDate} ${
|
||||
isDateRangeSelectorPopoverOpen ? classes.selectDateFocused : ''
|
||||
}`}
|
||||
onClick={() => setDateRangeSelectorPopoverOpen(true)}
|
||||
ref={dateRangeSelectorRef}
|
||||
aria-label="time range"
|
||||
aria-haspopup="true"
|
||||
>
|
||||
<Typography className={classes.displayDate}>
|
||||
{range.startDate === ' '
|
||||
? t('analyticsDashboard.dataSourceTable.selectPeriod')
|
||||
: `${range.startDate.split(' ')[2]} ${
|
||||
range.startDate.split(' ')[1]
|
||||
} ${range.startDate.split(' ')[3]} - ${
|
||||
range.endDate.split(' ')[2]
|
||||
} ${range.endDate.split(' ')[1]} ${
|
||||
range.endDate.split(' ')[3]
|
||||
}`}
|
||||
|
||||
<IconButton className={classes.rangeSelectorIcon}>
|
||||
{isDateRangeSelectorPopoverOpen ? (
|
||||
<KeyboardArrowDownIcon />
|
||||
) : (
|
||||
<ChevronRightIcon />
|
||||
)}
|
||||
</IconButton>
|
||||
</Typography>
|
||||
</Button>
|
||||
|
||||
<div className={classes.addButton}>
|
||||
<IconButton className={classes.rangeSelectorIcon}>
|
||||
{isDateRangeSelectorPopoverOpen ? (
|
||||
<KeyboardArrowUpIcon />
|
||||
) : (
|
||||
<KeyboardArrowDownIcon />
|
||||
)}
|
||||
</IconButton>
|
||||
</Typography>
|
||||
</Button>
|
||||
<ButtonFilled
|
||||
onClick={() => {
|
||||
onClick={() =>
|
||||
history.push({
|
||||
pathname: '/analytics/datasource/create',
|
||||
search: `?projectID=${projectID}&projectRole=${projectRole}`,
|
||||
});
|
||||
}}
|
||||
})
|
||||
}
|
||||
className={classes.addButton}
|
||||
>
|
||||
<Typography className={classes.dateRangeDefault}>
|
||||
<Typography className={classes.buttonText}>
|
||||
{t('analyticsDashboard.dataSourceTable.addDataSource')}
|
||||
</Typography>
|
||||
</ButtonFilled>
|
||||
<DateRangeSelector
|
||||
anchorEl={dateRangeSelectorRef.current as HTMLElement}
|
||||
isOpen={isDateRangeSelectorPopoverOpen}
|
||||
onClose={() => setDateRangeSelectorPopoverOpen(false)}
|
||||
callbackToSetRange={CallbackFromRangeSelector}
|
||||
/>
|
||||
</div>
|
||||
<DateRangeSelector
|
||||
anchorEl={dateRangeSelectorRef.current as HTMLElement}
|
||||
isOpen={isDateRangeSelectorPopoverOpen}
|
||||
onClose={() => {
|
||||
setDateRangeSelectorPopoverOpen(false);
|
||||
}}
|
||||
callbackToSetRange={CallbackFromRangeSelector}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
/* eslint-disable no-unused-expressions */
|
||||
import { useQuery } from '@apollo/client';
|
||||
import { useMutation, useQuery } from '@apollo/client';
|
||||
import {
|
||||
Drawer,
|
||||
Paper,
|
||||
Snackbar,
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
|
@ -10,13 +12,17 @@ import {
|
|||
TableRow,
|
||||
Typography,
|
||||
} from '@material-ui/core';
|
||||
import { Alert } from '@material-ui/lab';
|
||||
import { ButtonFilled, ButtonOutlined, TextButton } from 'litmus-ui';
|
||||
import moment from 'moment';
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import Loader from '../../../../components/Loader';
|
||||
import { DELETE_DATASOURCE } from '../../../../graphql';
|
||||
import { LIST_DATASOURCE } from '../../../../graphql/queries';
|
||||
import {
|
||||
DataSourceList,
|
||||
DeleteDataSourceInput,
|
||||
ListDataSourceResponse,
|
||||
ListDataSourceVars,
|
||||
} from '../../../../models/graphql/dataSourceDetails';
|
||||
|
@ -40,8 +46,6 @@ interface RangeType {
|
|||
interface SortData {
|
||||
lastConfigured: { sort: boolean; ascending: boolean };
|
||||
name: { sort: boolean; ascending: boolean };
|
||||
status: { sort: boolean; ascending: boolean };
|
||||
dataSourceType: { sort: boolean; ascending: boolean };
|
||||
}
|
||||
|
||||
interface Filter {
|
||||
|
@ -52,6 +56,12 @@ interface Filter {
|
|||
searchTokens: string[];
|
||||
}
|
||||
|
||||
interface ForceDeleteVars {
|
||||
connectedDashboards: string[];
|
||||
dsID: string;
|
||||
dsName: string;
|
||||
}
|
||||
|
||||
const DataSourceTable: React.FC = () => {
|
||||
const classes = useStyles();
|
||||
const { t } = useTranslation();
|
||||
|
@ -61,8 +71,6 @@ const DataSourceTable: React.FC = () => {
|
|||
sortData: {
|
||||
name: { sort: false, ascending: true },
|
||||
lastConfigured: { sort: true, ascending: false },
|
||||
status: { sort: false, ascending: true },
|
||||
dataSourceType: { sort: false, ascending: true },
|
||||
},
|
||||
selectedStatus: 'All',
|
||||
searchTokens: [''],
|
||||
|
@ -70,6 +78,17 @@ const DataSourceTable: React.FC = () => {
|
|||
const [page, setPage] = React.useState(0);
|
||||
const [rowsPerPage, setRowsPerPage] = React.useState(5);
|
||||
const projectID = getProjectID();
|
||||
const [success, setSuccess] = React.useState(false);
|
||||
const [isAlertOpen, setIsAlertOpen] = React.useState(false);
|
||||
const [drawerState, setDrawerState] = React.useState(false);
|
||||
const [showAllDashboards, setShowAllDashboards] = React.useState(false);
|
||||
const [forceDeleteVars, setForceDeleteVars] = React.useState<ForceDeleteVars>(
|
||||
{
|
||||
connectedDashboards: [],
|
||||
dsID: '',
|
||||
dsName: '',
|
||||
}
|
||||
);
|
||||
|
||||
const handleChangePage = (event: unknown, newPage: number) => {
|
||||
setPage(newPage);
|
||||
|
@ -83,15 +102,41 @@ const DataSourceTable: React.FC = () => {
|
|||
};
|
||||
|
||||
// Apollo query to get the data source data
|
||||
const { data, loading, error } = useQuery<DataSourceList, ListDataSourceVars>(
|
||||
LIST_DATASOURCE,
|
||||
const { data, loading, error, refetch } = useQuery<
|
||||
DataSourceList,
|
||||
ListDataSourceVars
|
||||
>(LIST_DATASOURCE, {
|
||||
variables: { projectID },
|
||||
fetchPolicy: 'cache-and-network',
|
||||
pollInterval: 10000,
|
||||
});
|
||||
|
||||
const alertStateHandler = (successState: boolean) => {
|
||||
setSuccess(successState);
|
||||
setIsAlertOpen(true);
|
||||
if (successState) {
|
||||
refetch();
|
||||
}
|
||||
};
|
||||
|
||||
const [deleteDataSource] = useMutation<boolean, DeleteDataSourceInput>(
|
||||
DELETE_DATASOURCE,
|
||||
{
|
||||
variables: { projectID },
|
||||
fetchPolicy: 'cache-and-network',
|
||||
pollInterval: 10000,
|
||||
onCompleted: () => alertStateHandler(true),
|
||||
onError: () => alertStateHandler(false),
|
||||
}
|
||||
);
|
||||
|
||||
const cleanDrawerState = () => {
|
||||
setForceDeleteVars({
|
||||
connectedDashboards: [],
|
||||
dsID: '',
|
||||
dsName: '',
|
||||
});
|
||||
setShowAllDashboards(false);
|
||||
setDrawerState(false);
|
||||
};
|
||||
|
||||
const getDataSourceType = (searchingData: ListDataSourceResponse[]) => {
|
||||
const uniqueList: string[] = [];
|
||||
searchingData.forEach((data) => {
|
||||
|
@ -116,13 +161,8 @@ const DataSourceTable: React.FC = () => {
|
|||
? !data.ListDataSource
|
||||
? []
|
||||
: data.ListDataSource.filter((ds: ListDataSourceResponse) => {
|
||||
return filter.searchTokens.every(
|
||||
(s: string) =>
|
||||
ds.ds_name.toLowerCase().includes(s) ||
|
||||
(ds.ds_type !== undefined &&
|
||||
ds.ds_type.toLowerCase().includes(s)) ||
|
||||
(ds.health_status !== undefined &&
|
||||
ds.health_status.toLowerCase().includes(s))
|
||||
return filter.searchTokens.every((s: string) =>
|
||||
ds.ds_name.toLowerCase().includes(s)
|
||||
);
|
||||
})
|
||||
.filter((data) => {
|
||||
|
@ -167,21 +207,6 @@ const DataSourceTable: React.FC = () => {
|
|||
? sortNumAsc(y, x)
|
||||
: sortNumDesc(y, x);
|
||||
}
|
||||
if (filter.sortData.status.sort) {
|
||||
const x = a.health_status;
|
||||
const y = b.health_status;
|
||||
return filter.sortData.status.ascending
|
||||
? sortAlphaAsc(x, y)
|
||||
: sortAlphaDesc(x, y);
|
||||
}
|
||||
if (filter.sortData.dataSourceType.sort) {
|
||||
const x = a.ds_type;
|
||||
const y = b.ds_type;
|
||||
|
||||
return filter.sortData.dataSourceType.ascending
|
||||
? sortAlphaAsc(x, y)
|
||||
: sortAlphaDesc(x, y);
|
||||
}
|
||||
return 0;
|
||||
})
|
||||
: [];
|
||||
|
@ -207,46 +232,50 @@ const DataSourceTable: React.FC = () => {
|
|||
.filter((s) => s !== ''),
|
||||
})
|
||||
}
|
||||
dataSourceTypes={getDataSourceType(payload)}
|
||||
statuses={getStatus(payload)}
|
||||
callbackToSetDataSourceType={(dataSourceType: string) => {
|
||||
dataSourceTypes={getDataSourceType(data?.ListDataSource ?? [])}
|
||||
statuses={getStatus(data?.ListDataSource ?? [])}
|
||||
callbackToSetDataSourceType={(dataSourceType: string) =>
|
||||
setFilter({
|
||||
...filter,
|
||||
selectedDataSourceType: dataSourceType,
|
||||
});
|
||||
}}
|
||||
callbackToSetStatus={(status: string) => {
|
||||
})
|
||||
}
|
||||
callbackToSetStatus={(status: string) =>
|
||||
setFilter({
|
||||
...filter,
|
||||
selectedStatus: status,
|
||||
});
|
||||
}}
|
||||
})
|
||||
}
|
||||
callbackToSetRange={(
|
||||
selectedStartDate: string,
|
||||
selectedEndDate: string
|
||||
) => {
|
||||
) =>
|
||||
setFilter({
|
||||
...filter,
|
||||
range: {
|
||||
startDate: selectedStartDate,
|
||||
endDate: selectedEndDate,
|
||||
},
|
||||
});
|
||||
}}
|
||||
})
|
||||
}
|
||||
/>
|
||||
</section>
|
||||
</Paper>
|
||||
<Paper>
|
||||
<section className="table section">
|
||||
<TableContainer className={classes.tableMain}>
|
||||
<TableContainer
|
||||
className={`${classes.tableMain} ${
|
||||
!payload.length || loading ? classes.minHeight : ''
|
||||
}`}
|
||||
>
|
||||
<Table aria-label="simple table">
|
||||
<TableHeader
|
||||
callBackToSort={(sortConfigurations: SortData) => {
|
||||
callBackToSort={(sortConfigurations: SortData) =>
|
||||
setFilter({
|
||||
...filter,
|
||||
sortData: sortConfigurations,
|
||||
});
|
||||
}}
|
||||
})
|
||||
}
|
||||
/>
|
||||
<TableBody>
|
||||
{error ? (
|
||||
|
@ -260,18 +289,29 @@ const DataSourceTable: React.FC = () => {
|
|||
) : loading ? (
|
||||
<TableRow>
|
||||
<TableCell colSpan={6}>
|
||||
<Loader />
|
||||
<Typography align="center">
|
||||
{t('analyticsDashboard.dataSourceTable.loading')}
|
||||
</Typography>
|
||||
<div
|
||||
className={`${classes.noRecords} ${classes.loading}`}
|
||||
>
|
||||
<Loader />
|
||||
<Typography align="center">
|
||||
{t('analyticsDashboard.dataSourceTable.loading')}
|
||||
</Typography>
|
||||
</div>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
) : !payload.length ? (
|
||||
<TableRow>
|
||||
<TableCell colSpan={6}>
|
||||
<Typography align="center">
|
||||
{t('analyticsDashboard.dataSourceTable.noRecords')}
|
||||
</Typography>
|
||||
<div className={classes.noRecords}>
|
||||
<img
|
||||
src="/icons/dataSourceUnavailable.svg"
|
||||
className={classes.unavailableIcon}
|
||||
alt="Data Source"
|
||||
/>
|
||||
<Typography className={classes.noRecordsText}>
|
||||
{t('analyticsDashboard.dataSourceTable.noRecords')}
|
||||
</Typography>
|
||||
</div>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
) : payload.length > 0 ? (
|
||||
|
@ -286,7 +326,22 @@ const DataSourceTable: React.FC = () => {
|
|||
key={data.ds_id}
|
||||
className={classes.tableRow}
|
||||
>
|
||||
<TableData data={data} />
|
||||
<TableData
|
||||
data={data}
|
||||
drawerStateHandler={(
|
||||
ds_id,
|
||||
ds_name,
|
||||
dashboards
|
||||
) => {
|
||||
setForceDeleteVars({
|
||||
connectedDashboards: dashboards,
|
||||
dsID: ds_id,
|
||||
dsName: ds_name,
|
||||
});
|
||||
setDrawerState(true);
|
||||
}}
|
||||
alertStateHandler={alertStateHandler}
|
||||
/>
|
||||
</TableRow>
|
||||
);
|
||||
})
|
||||
|
@ -311,9 +366,145 @@ const DataSourceTable: React.FC = () => {
|
|||
onChangePage={handleChangePage}
|
||||
onChangeRowsPerPage={handleChangeRowsPerPage}
|
||||
className={classes.tablePagination}
|
||||
SelectProps={{
|
||||
MenuProps: {
|
||||
anchorOrigin: {
|
||||
vertical: 'bottom',
|
||||
horizontal: 'right',
|
||||
},
|
||||
transformOrigin: {
|
||||
vertical: 'top',
|
||||
horizontal: 'right',
|
||||
},
|
||||
getContentAnchorEl: null,
|
||||
classes: { paper: classes.menuList },
|
||||
},
|
||||
}}
|
||||
classes={{ menuItem: classes.menuListItem }}
|
||||
/>
|
||||
</section>
|
||||
</Paper>
|
||||
{isAlertOpen && (
|
||||
<Snackbar
|
||||
open={isAlertOpen}
|
||||
autoHideDuration={3000}
|
||||
onClose={() => setIsAlertOpen(false)}
|
||||
>
|
||||
<Alert
|
||||
onClose={() => setIsAlertOpen(false)}
|
||||
severity={success ? 'success' : 'error'}
|
||||
>
|
||||
{success
|
||||
? t('analyticsDashboard.dataSourceTable.deletionSuccess')
|
||||
: t('analyticsDashboard.dataSourceTable.deletionError')}
|
||||
</Alert>
|
||||
</Snackbar>
|
||||
)}
|
||||
<Drawer
|
||||
className={classes.drawer}
|
||||
variant="persistent"
|
||||
anchor="right"
|
||||
open={drawerState}
|
||||
classes={{
|
||||
paper: classes.drawerPaper,
|
||||
}}
|
||||
ModalProps={{
|
||||
keepMounted: true,
|
||||
}}
|
||||
>
|
||||
<div className={classes.drawerContent}>
|
||||
<div className={classes.flexContainer}>
|
||||
<Typography className={classes.drawerHeading} align="left">
|
||||
{t('analyticsDashboard.dataSourceTable.delete')}
|
||||
<b>
|
||||
<i>{` ${forceDeleteVars.dsName} `}</i>
|
||||
</b>
|
||||
</Typography>
|
||||
<ButtonOutlined
|
||||
className={classes.closeButton}
|
||||
onClick={() => cleanDrawerState()}
|
||||
>
|
||||
✕
|
||||
</ButtonOutlined>
|
||||
</div>
|
||||
<blockquote className={classes.warningBlock}>
|
||||
<Typography className={classes.warningText} align="left">
|
||||
{t('analyticsDashboard.dataSourceTable.warning.text')}
|
||||
</Typography>
|
||||
</blockquote>
|
||||
<Typography className={classes.drawerBodyText} align="left">
|
||||
{t('analyticsDashboard.dataSourceTable.warning.info')}
|
||||
</Typography>
|
||||
<Typography
|
||||
className={classes.drawerBodyText}
|
||||
style={{ fontWeight: 500 }}
|
||||
align="left"
|
||||
>
|
||||
{t(
|
||||
'analyticsDashboard.dataSourceTable.warning.connectedDashboards'
|
||||
)}
|
||||
</Typography>
|
||||
<div className={classes.dashboardsList}>
|
||||
{(showAllDashboards
|
||||
? forceDeleteVars.connectedDashboards
|
||||
: forceDeleteVars.connectedDashboards.slice(0, 3)
|
||||
).map((name: string, index: number) => (
|
||||
<Typography
|
||||
className={`${classes.drawerBodyText} ${classes.drawerListItem}`}
|
||||
align="left"
|
||||
key={`${name}-dashboard`}
|
||||
>
|
||||
{`${index + 1}. ${name}`}
|
||||
</Typography>
|
||||
))}
|
||||
</div>
|
||||
{forceDeleteVars.connectedDashboards.length - 3 >= 1 && (
|
||||
<TextButton
|
||||
onClick={() => setShowAllDashboards(!showAllDashboards)}
|
||||
className={classes.cancelButton}
|
||||
variant="highlight"
|
||||
>
|
||||
<Typography className={classes.buttonText}>
|
||||
{showAllDashboards
|
||||
? t('analyticsDashboard.dataSourceTable.warning.showLess')
|
||||
: `+${forceDeleteVars.connectedDashboards.length - 3} ${t(
|
||||
'analyticsDashboard.dataSourceTable.warning.dashboards'
|
||||
)}`}
|
||||
</Typography>
|
||||
</TextButton>
|
||||
)}
|
||||
<div className={classes.flexButtons}>
|
||||
<TextButton
|
||||
onClick={() => cleanDrawerState()}
|
||||
className={classes.cancelButton}
|
||||
>
|
||||
<Typography className={classes.buttonText}>
|
||||
{t('analyticsDashboard.dataSourceTable.modal.cancel')}
|
||||
</Typography>
|
||||
</TextButton>
|
||||
<ButtonFilled
|
||||
onClick={() => {
|
||||
deleteDataSource({
|
||||
variables: {
|
||||
deleteDSInput: {
|
||||
ds_id: forceDeleteVars.dsID,
|
||||
force_delete: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
cleanDrawerState();
|
||||
}}
|
||||
variant="error"
|
||||
>
|
||||
<Typography
|
||||
className={`${classes.buttonText} ${classes.confirmButtonText}`}
|
||||
>
|
||||
{t('analyticsDashboard.dataSourceTable.modal.forceDelete')}
|
||||
</Typography>
|
||||
</ButtonFilled>
|
||||
</div>
|
||||
</div>
|
||||
</Drawer>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -21,7 +21,8 @@ const useStyles = makeStyles((theme) => ({
|
|||
flexDirection: 'row',
|
||||
alignContent: 'center',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
justifyContent: 'space-between',
|
||||
overflowX: 'auto',
|
||||
height: '6rem',
|
||||
backgroundColor: theme.palette.background.paper,
|
||||
},
|
||||
|
@ -30,40 +31,18 @@ const useStyles = makeStyles((theme) => ({
|
|||
height: '4.5rem',
|
||||
},
|
||||
|
||||
input: {
|
||||
'&:-webkit-autofill': {
|
||||
WebkitTextFillColor: theme.palette.text.primary,
|
||||
WebkitBoxShadow: `0 0 0 1000px ${theme.palette.background.paper} inset`,
|
||||
},
|
||||
},
|
||||
|
||||
search: {
|
||||
marginRight: 'auto',
|
||||
marginLeft: theme.spacing(6.25),
|
||||
borderBottom: `1px solid ${theme.palette.border.main}`,
|
||||
},
|
||||
|
||||
binIcon: {
|
||||
width: '1.5rem',
|
||||
height: '1.5rem',
|
||||
},
|
||||
|
||||
cogWheelIcon: {
|
||||
width: '1.5rem',
|
||||
height: '1.5rem',
|
||||
},
|
||||
|
||||
delete: {
|
||||
color: theme.palette.error.dark,
|
||||
marginLeft: theme.spacing(6),
|
||||
},
|
||||
|
||||
tableMain: {
|
||||
marginTop: theme.spacing(4.25),
|
||||
height: '30.5rem',
|
||||
height: '29.75rem',
|
||||
backgroundColor: theme.palette.background.paper,
|
||||
minHeight: '25rem',
|
||||
'&::-webkit-scrollbar': {
|
||||
width: '0.2em',
|
||||
height: '0.2em',
|
||||
},
|
||||
'&::-webkit-scrollbar-track': {
|
||||
webkitBoxShadow: `inset 0 0 6px ${theme.palette.common.black}`,
|
||||
|
@ -76,33 +55,58 @@ const useStyles = makeStyles((theme) => ({
|
|||
},
|
||||
},
|
||||
|
||||
tableHead: {
|
||||
opacity: 0.7,
|
||||
color: theme.palette.text.primary,
|
||||
minHeight: {
|
||||
height: '20rem',
|
||||
minHeight: '20rem',
|
||||
},
|
||||
|
||||
dataSourceName: {
|
||||
borderRight: `1px solid ${theme.palette.border.main}`,
|
||||
width: '12rem',
|
||||
noRecords: {
|
||||
height: '12.5rem',
|
||||
display: 'flex',
|
||||
padding: theme.spacing(7.5, 3, 5),
|
||||
justifyContent: 'center',
|
||||
},
|
||||
|
||||
dataSourceType: {
|
||||
paddingLeft: theme.spacing(10.5),
|
||||
width: '10rem',
|
||||
loading: {
|
||||
flexDirection: 'column',
|
||||
padding: theme.spacing(2, 3, 7.5),
|
||||
},
|
||||
|
||||
unavailableIcon: {
|
||||
height: '3.5rem',
|
||||
width: '3.5rem',
|
||||
marginTop: theme.spacing(0.65),
|
||||
},
|
||||
|
||||
noRecordsText: {
|
||||
color: theme.palette.text.hint,
|
||||
padding: theme.spacing(2),
|
||||
fontSize: '1.5rem',
|
||||
lineHeight: '150%',
|
||||
letterSpacing: '0.1176px',
|
||||
textAlign: 'center',
|
||||
},
|
||||
|
||||
dataSourceNameHead: {
|
||||
marginTop: theme.spacing(2.5),
|
||||
fontWeight: 500,
|
||||
fontSize: '0.75rem',
|
||||
lineHeight: '150%',
|
||||
color: theme.palette.text.hint,
|
||||
},
|
||||
|
||||
dataSourceStatusHead: {
|
||||
marginTop: theme.spacing(2.5),
|
||||
paddingLeft: theme.spacing(2),
|
||||
dataSourceNameHeadWithoutSort: {
|
||||
marginTop: theme.spacing(0.75),
|
||||
},
|
||||
|
||||
dataSourceStatusHeadWithoutSort: {
|
||||
marginTop: theme.spacing(0.75),
|
||||
paddingLeft: theme.spacing(4),
|
||||
},
|
||||
|
||||
headSpacing: {
|
||||
paddingLeft: theme.spacing(8),
|
||||
maxWidth: '5rem',
|
||||
minWidth: '5rem',
|
||||
paddingLeft: theme.spacing(2),
|
||||
},
|
||||
|
||||
nameContent: {
|
||||
|
@ -116,25 +120,15 @@ const useStyles = makeStyles((theme) => ({
|
|||
justifyContent: 'center',
|
||||
},
|
||||
|
||||
markerIconDown: {
|
||||
markerIcon: {
|
||||
color: theme.palette.text.hint,
|
||||
paddingTop: theme.spacing(0.5),
|
||||
margin: 0,
|
||||
},
|
||||
|
||||
markerIconUp: {
|
||||
color: theme.palette.text.hint,
|
||||
paddingTop: theme.spacing(0.5),
|
||||
margin: 0,
|
||||
},
|
||||
|
||||
tableDataStatus: {
|
||||
paddingLeft: theme.spacing(9),
|
||||
width: '8rem',
|
||||
},
|
||||
|
||||
options: {
|
||||
width: '5rem',
|
||||
minWidth: '3rem',
|
||||
paddingRight: theme.spacing(2),
|
||||
},
|
||||
|
||||
tablePagination: {
|
||||
|
@ -151,12 +145,19 @@ const useStyles = makeStyles((theme) => ({
|
|||
|
||||
selectDate: {
|
||||
display: 'flex',
|
||||
height: '2.9rem',
|
||||
height: '2.75rem',
|
||||
minWidth: '9rem',
|
||||
border: `0.1px solid ${theme.palette.border.main}`,
|
||||
borderRadius: 4,
|
||||
borderRadius: '0.25rem',
|
||||
marginRight: theme.spacing(1.5),
|
||||
textTransform: 'none',
|
||||
'&:hover': {
|
||||
borderColor: theme.palette.highlight,
|
||||
},
|
||||
},
|
||||
|
||||
selectDateFocused: {
|
||||
border: `2px solid ${theme.palette.highlight}`,
|
||||
},
|
||||
|
||||
rangeSelectorIcon: {
|
||||
|
@ -164,10 +165,63 @@ const useStyles = makeStyles((theme) => ({
|
|||
height: '0.625rem',
|
||||
},
|
||||
|
||||
tableDataStatus: {
|
||||
paddingLeft: theme.spacing(6),
|
||||
width: '5rem',
|
||||
},
|
||||
|
||||
tableObjects: {
|
||||
display: 'flex',
|
||||
gap: '0.5rem',
|
||||
textAlign: 'left',
|
||||
color: theme.palette.text.primary,
|
||||
fontSize: '0.75rem',
|
||||
lineHeight: '150%',
|
||||
},
|
||||
|
||||
dataSourceNameColData: {
|
||||
maxWidth: '10rem',
|
||||
fontWeight: 500,
|
||||
},
|
||||
|
||||
dataSourceUrlColData: {
|
||||
maxWidth: '8.5rem',
|
||||
overflow: 'hidden',
|
||||
textOverflow: 'ellipsis',
|
||||
whiteSpace: 'nowrap',
|
||||
},
|
||||
|
||||
inlineIcon: {
|
||||
margin: theme.spacing(0.25, 0),
|
||||
width: '1rem',
|
||||
height: '1rem',
|
||||
},
|
||||
button: {
|
||||
minWidth: 0,
|
||||
minHeight: 0,
|
||||
padding: 0,
|
||||
width: 'fit-content',
|
||||
'&:hover': {
|
||||
cursor: 'pointer !important',
|
||||
},
|
||||
},
|
||||
buttonLabel: {
|
||||
justifyContent: 'flex-start',
|
||||
marginLeft: theme.spacing(0.5),
|
||||
},
|
||||
|
||||
columnDivider: {
|
||||
borderRight: `1px solid ${theme.palette.border.main}`,
|
||||
},
|
||||
|
||||
dividerPadding: {
|
||||
paddingLeft: theme.spacing(4),
|
||||
},
|
||||
|
||||
// Form Select Properties
|
||||
formControl: {
|
||||
margin: theme.spacing(0.5),
|
||||
height: '2.8rem',
|
||||
height: '2.6rem',
|
||||
minWidth: '9rem',
|
||||
},
|
||||
|
||||
|
@ -185,49 +239,151 @@ const useStyles = makeStyles((theme) => ({
|
|||
padding: theme.spacing(0.4),
|
||||
},
|
||||
|
||||
dateRangeDefault: {
|
||||
height: '2rem',
|
||||
textDecoration: 'none',
|
||||
textTransform: 'none',
|
||||
padding: theme.spacing(1),
|
||||
},
|
||||
|
||||
addButton: {
|
||||
marginRight: theme.spacing(3),
|
||||
marginLeft: theme.spacing(1),
|
||||
margin: theme.spacing(0, 3, 0, 1),
|
||||
padding: theme.spacing(0, 2.5),
|
||||
},
|
||||
|
||||
icon: {
|
||||
width: '6rem',
|
||||
height: '6rem',
|
||||
// Menu option with icon
|
||||
menuItem: {
|
||||
width: '10rem',
|
||||
height: '2.5rem',
|
||||
'&:hover': {
|
||||
background: theme.palette.cards.highlight,
|
||||
},
|
||||
},
|
||||
|
||||
modalHeading: {
|
||||
marginTop: theme.spacing(3.5),
|
||||
fontSize: '2.25rem',
|
||||
marginBottom: theme.spacing(4.5),
|
||||
headerIcon: {
|
||||
color: theme.palette.border.main,
|
||||
},
|
||||
|
||||
modalBody: {
|
||||
marginBottom: theme.spacing(4.5),
|
||||
},
|
||||
|
||||
closeButton: {
|
||||
borderColor: theme.palette.border.main,
|
||||
},
|
||||
|
||||
flexButtons: {
|
||||
expDiv: {
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-evenly',
|
||||
},
|
||||
btnImg: {
|
||||
width: '0.8125rem',
|
||||
height: '0.8125rem',
|
||||
marginTop: theme.spacing(0.375),
|
||||
},
|
||||
btnText: {
|
||||
paddingLeft: theme.spacing(1.625),
|
||||
},
|
||||
deleteText: {
|
||||
color: theme.palette.error.main,
|
||||
},
|
||||
|
||||
buttonOutlineWarning: {
|
||||
borderColor: theme.palette.error.dark,
|
||||
// modal
|
||||
modalHeading: {
|
||||
fontSize: '1.5rem',
|
||||
lineHeight: '130%',
|
||||
fontWeight: 'bold',
|
||||
fontFeatureSettings: 'pnum on, lnum on',
|
||||
margin: theme.spacing(2.5, 0, 4.5),
|
||||
padding: theme.spacing(0, 6.5),
|
||||
},
|
||||
modalBodyText: {
|
||||
fontSize: '1rem',
|
||||
lineHeight: '130%',
|
||||
padding: theme.spacing(0, 6.5),
|
||||
},
|
||||
flexButtons: {
|
||||
display: 'flex',
|
||||
justifyContent: 'flex-end',
|
||||
marginTop: 'auto',
|
||||
},
|
||||
flexButtonsPadding: {
|
||||
padding: theme.spacing(5.5, 6.5, 0, 0),
|
||||
},
|
||||
|
||||
modal: {
|
||||
padding: theme.spacing(15, 0),
|
||||
padding: theme.spacing(5, 0),
|
||||
},
|
||||
buttonText: {
|
||||
lineHeight: '140%',
|
||||
fontSize: '0.875rem',
|
||||
},
|
||||
confirmButtonText: {
|
||||
color: theme.palette.text.secondary,
|
||||
padding: theme.spacing(0, 3),
|
||||
},
|
||||
cancelButton: {
|
||||
width: 'fit-content',
|
||||
marginRight: theme.spacing(1.5),
|
||||
padding: theme.spacing(0, 3),
|
||||
},
|
||||
|
||||
// drawer
|
||||
drawer: {
|
||||
width: 'fit-content',
|
||||
flexShrink: 0,
|
||||
},
|
||||
drawerPaper: {
|
||||
width: '100%',
|
||||
background: 'rgba(0, 0, 0, 0.6)',
|
||||
},
|
||||
drawerContent: {
|
||||
height: '100%',
|
||||
width: '50%',
|
||||
marginLeft: '50%',
|
||||
background: theme.palette.background.paper,
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
padding: theme.spacing(10, 6.5),
|
||||
},
|
||||
closeButton: {
|
||||
borderColor: theme.palette.border.main,
|
||||
color: theme.palette.border.main,
|
||||
padding: theme.spacing(0.25, 2),
|
||||
minWidth: 0,
|
||||
},
|
||||
flexContainer: {
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between',
|
||||
},
|
||||
drawerHeading: {
|
||||
fontSize: '1.5rem',
|
||||
lineHeight: '130%',
|
||||
fontFeatureSettings: 'pnum on, lnum on',
|
||||
},
|
||||
warningBlock: {
|
||||
margin: theme.spacing(5, 0),
|
||||
padding: theme.spacing(0.5, 3),
|
||||
borderLeft: `5px solid ${theme.palette.warning.main}`,
|
||||
borderRadius: '5px 0 0 5px',
|
||||
background: theme.palette.warning.light,
|
||||
},
|
||||
warningText: {
|
||||
margin: theme.spacing(1.5, 0),
|
||||
fontSize: '0.875rem',
|
||||
lineHeight: '143%',
|
||||
},
|
||||
drawerBodyText: {
|
||||
fontSize: '1rem',
|
||||
lineHeight: '150%',
|
||||
marginBottom: theme.spacing(3),
|
||||
},
|
||||
dashboardsList: {
|
||||
maxHeight: '12rem',
|
||||
overflowY: 'scroll',
|
||||
marginBottom: theme.spacing(3),
|
||||
},
|
||||
drawerListItem: {
|
||||
marginBottom: theme.spacing(2),
|
||||
},
|
||||
|
||||
// select
|
||||
menuList: {
|
||||
boxShadow: '0 5px 9px rgba(0, 0, 0, 0.1)',
|
||||
},
|
||||
menuListItem: {
|
||||
background: `${theme.palette.background.paper} !important`,
|
||||
fontSize: '0.875rem',
|
||||
lineHeight: '150%',
|
||||
height: '1.875rem',
|
||||
'&:hover': {
|
||||
background: `${theme.palette.cards.highlight} !important`,
|
||||
},
|
||||
'&.Mui-selected': {
|
||||
background: `${theme.palette.cards.highlight} !important`,
|
||||
},
|
||||
},
|
||||
}));
|
||||
|
||||
|
@ -237,12 +393,12 @@ export const useOutlinedInputStyles = makeStyles((theme: Theme) => ({
|
|||
borderColor: theme.palette.border.main,
|
||||
},
|
||||
'&:hover $notchedOutline': {
|
||||
borderColor: theme.palette.border.main,
|
||||
borderColor: theme.palette.highlight,
|
||||
},
|
||||
'&$focused $notchedOutline': {
|
||||
borderColor: theme.palette.border.main,
|
||||
borderColor: theme.palette.highlight,
|
||||
},
|
||||
height: '2.9rem',
|
||||
height: '2.75rem',
|
||||
},
|
||||
focused: {},
|
||||
notchedOutline: {},
|
||||
|
|
|
@ -184,7 +184,7 @@ const ApplicationDashboardCard: React.FC<ApplicationDashboardCardProps> = ({
|
|||
>
|
||||
<AnalyticsIcon />
|
||||
</IconButton>
|
||||
<Typography>Analytics</Typography>
|
||||
<Typography align="center">View</Typography>
|
||||
</div>
|
||||
<div className={classes.cardActions}>
|
||||
<IconButton
|
||||
|
@ -201,13 +201,13 @@ const ApplicationDashboardCard: React.FC<ApplicationDashboardCardProps> = ({
|
|||
>
|
||||
<CogwheelIcon />
|
||||
</IconButton>
|
||||
<Typography>Configure</Typography>
|
||||
<Typography align="center">Configure</Typography>
|
||||
</div>
|
||||
<div className={classes.cardActions}>
|
||||
<IconButton onClick={() => downloadJSON()}>
|
||||
<DownloadIcon />
|
||||
</IconButton>
|
||||
<Typography>JSON</Typography>
|
||||
<Typography align="center">JSON</Typography>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
|
|
|
@ -9,7 +9,7 @@ const useStyles = makeStyles((theme) => ({
|
|||
|
||||
'&:hover': {
|
||||
transform: `translateY(-10px)`,
|
||||
boxShadow: `0px 1.2px 3.6px rgba(0, 0, 0, 0.1), 0px 6.4px 14.4px rgba(0, 0, 0, 0.13)`,
|
||||
boxShadow: `0 1.2px 3.6px rgba(0, 0, 0, 0.1), 0 6.4px 14.4px rgba(0, 0, 0, 0.13)`,
|
||||
borderRadius: '0.1875rem',
|
||||
},
|
||||
},
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
import { IconButton, Typography } from '@material-ui/core';
|
||||
import React from 'react';
|
||||
import { WorkflowRun } from '../../../../models/graphql/workflowData';
|
||||
import useActions from '../../../../redux/actions';
|
||||
import * as NodeSelectionActions from '../../../../redux/actions/nodeSelection';
|
||||
import { history } from '../../../../redux/configureStore';
|
||||
import { ReactComponent as AnalyticsIcon } from '../../../../svg/analytics.svg';
|
||||
import { ReactComponent as WorkflowRunIcon } from '../../../../svg/workflowRun.svg';
|
||||
import timeDifferenceForDate from '../../../../utils/datesModifier';
|
||||
import {
|
||||
getProjectID,
|
||||
|
@ -25,9 +28,9 @@ const WorkflowDashboardCard: React.FC<WorkflowDashboardCardProps> = ({
|
|||
data,
|
||||
}) => {
|
||||
const classes = useStyles();
|
||||
|
||||
const projectID = getProjectID();
|
||||
const projectRole = getProjectRole();
|
||||
const nodeSelection = useActions(NodeSelectionActions);
|
||||
|
||||
function getStatusVariant(phase: string) {
|
||||
switch (phase) {
|
||||
|
@ -73,6 +76,23 @@ const WorkflowDashboardCard: React.FC<WorkflowDashboardCardProps> = ({
|
|||
{timeDifferenceForDate(data.last_updated)}
|
||||
</Typography>
|
||||
<section className={classes.cardActionsSection}>
|
||||
<div className={classes.cardActions}>
|
||||
<IconButton
|
||||
onClick={() => {
|
||||
nodeSelection.selectNode({
|
||||
pod_name: '',
|
||||
});
|
||||
if (data.phase?.toLowerCase() !== 'notavailable')
|
||||
history.push({
|
||||
pathname: `/workflows/${data.workflow_run_id}`,
|
||||
search: `?projectID=${projectID}&projectRole=${projectRole}`,
|
||||
});
|
||||
}}
|
||||
>
|
||||
<WorkflowRunIcon />
|
||||
</IconButton>
|
||||
<Typography align="center">See workflow run</Typography>
|
||||
</div>
|
||||
<div className={classes.cardActions}>
|
||||
<IconButton
|
||||
onClick={() => {
|
||||
|
@ -84,7 +104,7 @@ const WorkflowDashboardCard: React.FC<WorkflowDashboardCardProps> = ({
|
|||
>
|
||||
<AnalyticsIcon />
|
||||
</IconButton>
|
||||
<Typography>See Analytics</Typography>
|
||||
<Typography align="center">See analytics</Typography>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
|
|
|
@ -9,7 +9,7 @@ const useStyles = makeStyles((theme) => ({
|
|||
|
||||
'&:hover': {
|
||||
transform: `translateY(-10px)`,
|
||||
boxShadow: `0px 1.2px 3.6px rgba(0, 0, 0, 0.1), 0px 6.4px 14.4px rgba(0, 0, 0, 0.13)`,
|
||||
boxShadow: `0 1.2px 3.6px rgba(0, 0, 0, 0.1), 0 6.4px 14.4px rgba(0, 0, 0, 0.13)`,
|
||||
borderRadius: '0.1875rem',
|
||||
},
|
||||
},
|
||||
|
|
|
@ -242,7 +242,7 @@ const Overview: React.FC = () => {
|
|||
src="./icons/dashboardCloud.svg"
|
||||
alt="Schedule a workflow"
|
||||
heading="Configure a chaos interleaved dashboard"
|
||||
description="Data source(s) have been found to be connected in this project. Select “Add dashboard” to configure a chaos interleaved dashboard"
|
||||
description="Data source(s) have been found to be connected in this project. Select “Create dashboard” to configure a chaos interleaved dashboard"
|
||||
button={
|
||||
<ButtonFilled
|
||||
onClick={() => {
|
||||
|
@ -252,14 +252,14 @@ const Overview: React.FC = () => {
|
|||
});
|
||||
}}
|
||||
>
|
||||
<Typography>Add dashboard</Typography>
|
||||
<Typography>Create dashboard</Typography>
|
||||
</ButtonFilled>
|
||||
}
|
||||
/>
|
||||
)}{' '}
|
||||
{workflowDashboardCount > 0 ? (
|
||||
<RecentOverviewContainer
|
||||
heading="Recent Workflow Dashboards"
|
||||
heading="Recently updated workflow dashboards"
|
||||
buttonLink="/create-workflow"
|
||||
buttonImgSrc="./icons/calendarBlank.svg"
|
||||
buttonImgAlt="Schedule workflow"
|
||||
|
@ -308,11 +308,11 @@ const Overview: React.FC = () => {
|
|||
)}
|
||||
{applicationDashboardCount > 0 && (
|
||||
<RecentOverviewContainer
|
||||
heading="Recent Application Dashboards"
|
||||
heading="Recently viewed application dashboards"
|
||||
buttonLink="/analytics/dashboard/create"
|
||||
buttonImgSrc="./icons/cloudWhite.svg"
|
||||
buttonImgAlt="Add dashboard"
|
||||
buttonText="Add dashbaord"
|
||||
buttonImgAlt="Create dashboard"
|
||||
buttonText="Create dashbaord"
|
||||
>
|
||||
{dashboardListLoading ? (
|
||||
<Center>
|
||||
|
|