Skip to content

Commit

Permalink
Polishing rendering of vulnerability view part #1 (#2490)
Browse files Browse the repository at this point in the history
### What's done:
- Just rendering of html/css
- +2 animations added
  • Loading branch information
orchestr7 authored Aug 24, 2023
1 parent a9091fd commit d469e0c
Show file tree
Hide file tree
Showing 10 changed files with 264 additions and 232 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ To build the project and run all tests, execute `./gradlew build`.
For more detailed instructions, including **deployment instructions**, see [save-deploy/README.md](save-deploy/README.md).

## Local deployment
0. Install Java 17 (LTS). We recommend [azul](https://www.azul.com/downloads/#downloads-table-zulu).
1. Ensure that docker daemon is running and `docker compose` is installed. We suggest [Docker Desktop](https://www.docker.com/products/docker-desktop/).
2. Run `./gradlew deployLocal -Psave.profile=dev` to start the MySql DB, Minio and will run all Spring Microservices with Docker Compose.
3. Run `./gradlew -Psave.profile=dev :save-frontend:run` to start save-frontend using webpack-dev-server, requests to REST API will be
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,17 @@

package com.saveourtool.save.frontend.components.basic

import com.saveourtool.save.frontend.externals.fontawesome.faPlus
import com.saveourtool.save.frontend.utils.buttonBuilder
import js.core.jso
import react.*
import react.dom.html.ReactHTML.div
import web.cssom.*

import kotlinx.datetime.LocalDateTime

const val HOVERABLE_CONST = "hoverable"

val timelineComponent: FC<TimelineComponentProps> = FC { props ->
val hoverable = props.onNodeClick?.let { "hoverable" }.orEmpty()
val hoverable = props.onNodeClick?.let { HOVERABLE_CONST }.orEmpty()

div {
className = ClassName("mb-3")
Expand All @@ -22,50 +22,51 @@ val timelineComponent: FC<TimelineComponentProps> = FC { props ->
+title
}
}
div {
className = ClassName("p-0 timeline-container")

props.onAddClick?.let { onClickCallback ->
buttonBuilder(
label = "Add date",
style = "secondary",
isOutline = true,
classes = "btn btn-primary"
) {
onClickCallback()
}
}

if (props.dates.isNotEmpty()) {
div {
className = ClassName("steps-container")
className = ClassName("p-0 timeline-container")
div {
style = jso {
position = "absolute".unsafeCast<Position>()
right = "1%".unsafeCast<Right>()
top = "0%".unsafeCast<Top>()
zIndex = "4".unsafeCast<ZIndex>()
}
props.onAddClick?.let { onClickCallback ->
buttonBuilder(faPlus, style = "secondary", isOutline = true, classes = "rounded-circle btn-sm mt-1 mr-1") {
onClickCallback()
}
className = ClassName("steps-container")
div {
className = ClassName("line")
}
}
div {
className = ClassName("line completed")
}
props.dates.toList()
.sortedBy { it.second }
.forEach { (label, dateTime) ->
div {
className = ClassName("step completed $hoverable")
props.onNodeClick?.let { onClickCallback ->
style = jso { cursor = "pointer".unsafeCast<Cursor>() }
onClick = { onClickCallback(dateTime, label) }
}
props.dates.toList()
.sortedBy { it.second }
.forEach { (label, dateTime) ->
div {
className = ClassName("text-label completed")
+label
className = ClassName("step $hoverable")
props.onNodeClick?.let { onClickCallback ->
onClick = { onClickCallback(dateTime, label) }
}

div {
className = ClassName("text-label")
+label
}
div {
className = ClassName("date-label")
+dateTime.date.toString()
}
}
div {
className = ClassName("date-label completed")
+dateTime.date.toString()
className = ClassName("line")
}
}
div {
className = ClassName("line completed")
}
div {
className = ClassName("line-end")
}
div {
className = ClassName("line-end")
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,42 +8,71 @@ import com.saveourtool.save.frontend.themes.Colors
import js.core.jso
import react.FC
import react.Props
import react.dom.html.ReactHTML.a
import react.dom.html.ReactHTML.div
import react.dom.html.ReactHTML.h4
import web.cssom.*
import web.cssom.TextDecoration.Companion.underline

private const val FOR_GREEN = 34
private const val FOR_YELLOW = 67
private const val MAX_VALUE = 100

val vulnerabilityBadge: FC<VulnerabilityBadgeProps> = FC { props ->
val (color, criticalityLabel) = when (props.vulnerability.progress) {
in 0..FOR_GREEN -> Colors.SUCCESS.value to "Low"
in FOR_GREEN..FOR_YELLOW -> Colors.WARNING.value to "Moderate"
in FOR_YELLOW..MAX_VALUE -> Colors.DANGER.value to "Critical"
else -> throw IllegalStateException("Progress should be in [0; 100], got ${props.vulnerability.progress}")
}
div {
className = ClassName("col")
val (color, criticalityLabel) = when (props.vulnerability.progress) {
in 0..FOR_GREEN -> Colors.SUCCESS.value to "Low"
in FOR_GREEN..FOR_YELLOW -> Colors.WARNING.value to "Moderate"
in FOR_YELLOW..MAX_VALUE -> Colors.DANGER.value to "Critical"
else -> throw IllegalStateException("Progress should be in [0; 100], got ${props.vulnerability.progress}")
className = ClassName("card shadow")
style = jso {
height = HEADER_HEIGHT.unsafeCast<Height>()
}

div {
className = ClassName("card-body")
div {
className = ClassName("row mb-2")
className = ClassName("row")
div {
className = ClassName("mx-auto")
className = ClassName("col-3 mr-1")
@Suppress("MAGIC_NUMBER", "MagicNumber")
progressBar(props.vulnerability.progress, color = color, size = 150, lineWidth = 45)
div {
className = ClassName("text-center font-weight-bold text-uppercase mx-auto p-1")
style = jso {
border = "1px solid $color".unsafeCast<Border>()
borderRadius = "1rem".unsafeCast<BorderRadius>()
this.color = color.unsafeCast<ColorProperty>()
position = "absolute".unsafeCast<Position>()
top = "70%".unsafeCast<Top>()
left = "50%".unsafeCast<Left>()
transform = "translate(-50%, -50%)".unsafeCast<Transform>()
className = ClassName("row")
progressBar(props.vulnerability.progress, color = color, size = "6.5rem", lineWidth = "3.5rem")
}
}
div {
className = ClassName("col-6")
div {
className = ClassName("row align-items-center mb-2")
h4 {
className = ClassName("text-gray-900 mb-2")
+"Criticality Scoring"
}
}
div {
className = ClassName("row align-items-center mb-2")
div {
className = ClassName("text-center text-xs font-weight-bold text-uppercase p-1")
style = jso {
border = "0.1rem solid $color".unsafeCast<Border>()
borderRadius = "1rem".unsafeCast<BorderRadius>()
this.color = color.unsafeCast<ColorProperty>()
}
+criticalityLabel
}
}
div {
className = ClassName("row")
a {
className = ClassName("nav-link text-xs d-flex pl-0 active")
href = "https://nvd.nist.gov/vuln-metrics/cvss/v3-calculator"
style = jso {
textDecoration = underline
}
+"NVD CVSS"
}
+criticalityLabel
}
}
}
Expand All @@ -59,4 +88,9 @@ external interface VulnerabilityBadgeProps : Props {
* [VulnerabilityDto] to display
*/
var vulnerability: VulnerabilityDto

/**
* horizontal alignment
*/
var horizontalAlignment: String
}
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,13 @@ val vulnerabilityGeneralInfo: FC<VulnerabilityGeneralInfo> = FC { props ->
div {
className = ClassName("card-body")
div {
className = ClassName("row")
className = ClassName("row mb-4")
div {
className = ClassName("ml-3 font-weight-bold text-primary text-uppercase mb-4")
+name
className = ClassName("col-6 align-self-center")
div {
className = ClassName("font-weight-bold text-primary-blue text-uppercase")
+name
}
}
if (isEditDisabled) {
buttonBuilder(
Expand All @@ -73,7 +76,7 @@ val vulnerabilityGeneralInfo: FC<VulnerabilityGeneralInfo> = FC { props ->
}
}
textarea {
className = ClassName("auto_height form-control-plaintext pt-0 pb-0")
className = ClassName("auto_height form-control-plaintext pt-0 pb-0 text-gray-900")
value = shortDescription
rows = 2
}
Expand Down Expand Up @@ -102,11 +105,11 @@ val vulnerabilityGeneralInfo: FC<VulnerabilityGeneralInfo> = FC { props ->
}
hr { }
h6 {
className = ClassName("font-weight-bold text-primary mb-4")
className = ClassName("font-weight-bold text-primary-blue mb-4")
+"Description"
}
textarea {
className = ClassName("auto_height form-control-plaintext pt-0 pb-0")
className = ClassName("auto_height form-control-plaintext pt-0 pb-0 text-gray-900")
value = vulnerability.description
disabled = isEditDisabled
rows = 8
Expand All @@ -121,7 +124,7 @@ val vulnerabilityGeneralInfo: FC<VulnerabilityGeneralInfo> = FC { props ->
if (!vulnerabilityIdentifier.isNullOrEmpty()) {
hr { }
h6 {
className = ClassName("font-weight-bold text-primary mb-4")
className = ClassName("font-weight-bold text-primary-blue mb-4")
+"Original Identifier"
}
div {
Expand All @@ -131,7 +134,7 @@ val vulnerabilityGeneralInfo: FC<VulnerabilityGeneralInfo> = FC { props ->
if (props.canEditVulnerability || props.vulnerability.tags.isNotEmpty()) {
hr { }
h6 {
className = ClassName("font-weight-bold text-primary mb-4")
className = ClassName("font-weight-bold text-primary-blue mb-4")
+"Tags"
}
div {
Expand All @@ -146,7 +149,7 @@ val vulnerabilityGeneralInfo: FC<VulnerabilityGeneralInfo> = FC { props ->
if (!relatedLink.isNullOrEmpty()) {
hr { }
h6 {
className = ClassName("font-weight-bold text-primary mb-4")
className = ClassName("font-weight-bold text-primary-blue mb-4")
+"Related link"
}
Link {
Expand All @@ -157,7 +160,7 @@ val vulnerabilityGeneralInfo: FC<VulnerabilityGeneralInfo> = FC { props ->
userInfo.run {
hr { }
h6 {
className = ClassName("font-weight-bold text-primary mb-3")
className = ClassName("font-weight-bold text-primary-blue mb-3")
+"Author"
}
renderUserAvatarWithName(this@run, isHorizontal = true, classes = "mr-2") {
Expand All @@ -169,7 +172,7 @@ val vulnerabilityGeneralInfo: FC<VulnerabilityGeneralInfo> = FC { props ->
organization?.run {
hr { }
h6 {
className = ClassName("font-weight-bold text-primary mb-3")
className = ClassName("font-weight-bold text-primary-blue mb-3")
+"Organization"
}
Link {
Expand All @@ -184,7 +187,7 @@ val vulnerabilityGeneralInfo: FC<VulnerabilityGeneralInfo> = FC { props ->
if (participants.isNotEmpty()) {
hr { }
h6 {
className = ClassName("font-weight-bold text-primary mb-4")
className = ClassName("font-weight-bold text-primary-blue mb-4")
+"Collaborators"
}
userBoard {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@ val vulnerabilityInfoTab: FC<VulnerabilityInfoTabProps> = FC { props ->

val (deleteProject, setDeleteProject) = useState<VulnerabilityProjectDto?>(null)

val isAbleToEdit = props.currentUserInfo.isSuperAdmin() || props.currentUserInfo in props.vulnerability.getAllParticipants()
val isAbleToEdit =
props.currentUserInfo.isSuperAdmin() || props.currentUserInfo in props.vulnerability.getAllParticipants()

val (vulnerabilityProjects, setVulnerabilityProjects) = useState<List<VulnerabilityProjectDto>>(emptyList())

Expand Down Expand Up @@ -176,7 +177,8 @@ val vulnerabilityInfoTab: FC<VulnerabilityInfoTabProps> = FC { props ->
}
}

if (props.vulnerability.dates.isNotEmpty()) {
// if dates are empty - we will not show the timeline for non-owners
if (props.vulnerability.dates.isNotEmpty() || isAbleToEdit) {
timelineComponent {
dates = props.vulnerability.getDatesWithLabels()
onAddClick = { dateWindowOpenness.openWindow() }
Expand All @@ -193,8 +195,6 @@ val vulnerabilityInfoTab: FC<VulnerabilityInfoTabProps> = FC { props ->
}
.takeIf { isAbleToEdit }
}
} else {
renderPlaceholder(isAbleToEdit, "No dates mentioned") { dateWindowOpenness.openWindow() }
}

renderProjects(
Expand Down Expand Up @@ -274,7 +274,7 @@ private fun ChildrenBuilder.renderProjects(
div {
className = ClassName("mt-4")
div {
className = ClassName("mb-3 text-xs text-center font-weight-bold text-primary text-uppercase")
className = ClassName("mb-3 text-xs text-center font-weight-bold text-primary-blue text-uppercase")
+sectionName
}
val data = projects.filter { it.type == projectType }
Expand Down Expand Up @@ -306,7 +306,7 @@ private fun ChildrenBuilder.renderProjectCard(project: VulnerabilityProjectDto)
div {
className = ClassName("card card-body")
h4 {
className = ClassName("text-center text-primary")
className = ClassName("text-center text-primary-blue")
Link {
className = ClassName("mb-2")
to = project.url
Expand Down Expand Up @@ -345,7 +345,7 @@ private fun ChildrenBuilder.renderPlaceholder(
) {
if (isAbleToEdit) {
div {
className = ClassName("d-flex justify-content-center")
className = ClassName("d-flex justify-content-center vulnerability-placeholder")
onClick = { onClickFun() }
style = jso {
cursor = "pointer".unsafeCast<Cursor>()
Expand Down
Loading

0 comments on commit d469e0c

Please sign in to comment.