@@ -25,6 +25,7 @@ import com.squareup.invert.models.StatMetadata
2525import org.jetbrains.compose.web.dom.H1
2626import org.jetbrains.compose.web.dom.H3
2727import org.jetbrains.compose.web.dom.H4
28+ import org.jetbrains.compose.web.dom.H5
2829import org.jetbrains.compose.web.dom.Li
2930import org.jetbrains.compose.web.dom.P
3031import org.jetbrains.compose.web.dom.Text
@@ -46,6 +47,7 @@ data class CodeReferencesNavRoute(
4647 val module : String? = null ,
4748 val treemap : Boolean? = null ,
4849 val chart : Boolean? = null ,
50+ val extras : Map <String , String >? = null
4951) : BaseNavRoute(CodeReferencesReportPage .navPage) {
5052
5153 override fun toSearchParams (): Map <String , String > = toParamsWithOnlyPageId(this )
@@ -65,6 +67,11 @@ data class CodeReferencesNavRoute(
6567 chart?.let {
6668 params[CHART_PARAM ] = chart.toString()
6769 }
70+ extras?.let {
71+ it.forEach { (key, value) ->
72+ params[" extra_$key " ] = value
73+ }
74+ }
6875 }
6976
7077 companion object {
@@ -74,37 +81,51 @@ data class CodeReferencesNavRoute(
7481 private const val OWNER_PARAM = " owner"
7582 private const val MODULE_PARAM = " module"
7683 private const val TREEMAP_PARAM = " treemap"
84+ private const val EXTRAS_PARAM_PREFIX = " extra_"
85+
7786
7887 fun parser (params : Map <String , String ?>): NavRoute {
79- val statKey = params[STATKEY_PARAM ]
80- val owner = params[OWNER_PARAM ]?.trim()?.let {
81- if (it.isNotBlank()) {
82- it
83- } else {
84- null
85- }
86- }
87- val module = params[MODULE_PARAM ]?.trim()?.let {
88- if (it.isNotBlank()) {
89- it
90- } else {
91- null
92- }
93- }
94- val treemap = params[TREEMAP_PARAM ]?.trim()?.let {
95- if (it.isNotBlank()) {
96- it.toBoolean()
97- } else {
98- null
88+ var statKey: String? = null
89+ var owner: String? = null
90+ var module: String? = null
91+ var treemap: Boolean? = null
92+ var chart: Boolean? = null
93+ var extras: MutableMap <String , String > = mutableMapOf ()
94+ params.forEach { (key, value) ->
95+ val trimmedValue = value?.trim()?.let {
96+ if (it.isNotBlank()) {
97+ it
98+ } else {
99+ null
100+ }
99101 }
100- }
101- val chart = params[CHART_PARAM ]?.trim()?.let {
102- if (it.isNotBlank()) {
103- it.toBoolean()
104- } else {
105- null
102+ if (trimmedValue != null ) {
103+ when (key) {
104+ STATKEY_PARAM -> {
105+ statKey = trimmedValue
106+ }
107+ OWNER_PARAM -> {
108+ owner = trimmedValue
109+ }
110+ MODULE_PARAM -> {
111+ module = trimmedValue
112+ }
113+ TREEMAP_PARAM -> {
114+ treemap = trimmedValue.toBoolean()
115+ }
116+ CHART_PARAM -> {
117+ chart = trimmedValue.toBoolean()
118+ }
119+ else -> {
120+ if (key.startsWith(EXTRAS_PARAM_PREFIX )) {
121+ val extraKey = key.substring(EXTRAS_PARAM_PREFIX .length)
122+ extras[extraKey] = trimmedValue
123+ }
124+ }
125+ }
106126 }
107127 }
128+
108129 return if (statKey == null ) {
109130 AllStatsNavRoute ()
110131 } else {
@@ -114,6 +135,7 @@ data class CodeReferencesNavRoute(
114135 module = module,
115136 treemap = treemap,
116137 chart = chart,
138+ extras = extras.let { if (it.isEmpty()) null else it }
117139 )
118140 }
119141 }
@@ -293,6 +315,18 @@ fun CodeReferencesComposable(
293315 true
294316 }
295317 }
318+ // Filter by extras
319+ .filter { ownerAndCodeReference: ModuleOwnerAndCodeReference ->
320+ val codeReference = ownerAndCodeReference.codeReference
321+ val extras = codeReferencesNavRoute.extras ? : mapOf ()
322+ if (extras.isEmpty()) {
323+ true
324+ } else {
325+ extras.all { (key, value) ->
326+ codeReference.extras[key] == value
327+ }
328+ }
329+ }
296330
297331 if (codeReferencesNavRoute.treemap == true ) {
298332 BootstrapRow {
@@ -337,12 +371,18 @@ fun CodeReferencesComposable(
337371 }
338372 }
339373
374+ BootstrapRow {
375+ H3 {
376+ Text (" Filters" )
377+ }
378+ }
379+
340380 val codeReferencesByOwner = allCodeReferencesForStat.groupBy { it.owner }
341381 val totalCodeReferenceCount = allCodeReferencesForStat.size
342382 BootstrapRow {
343383 BootstrapColumn (6 ) {
344- H3 {
345- Text (" Filter by Owner" )
384+ H5 {
385+ Text (" Owner" )
346386 BootstrapSelectDropdown (
347387 placeholderText = " -- All Owners ($totalCodeReferenceCount Total) --" ,
348388 currentValue = codeReferencesNavRoute.owner,
@@ -364,8 +404,8 @@ fun CodeReferencesComposable(
364404 val codeReferencesByModule =
365405 allCodeReferencesForStat.groupBy { it.module }
366406 BootstrapColumn (6 ) {
367- H3 {
368- Text (" Filter by Module" )
407+ H5 {
408+ Text (" Module" )
369409 BootstrapSelectDropdown (
370410 placeholderText = " -- All Modules --" ,// (${codeReferencesByModule.size} Total) --",
371411 currentValue = codeReferencesNavRoute.module,
@@ -385,6 +425,58 @@ fun CodeReferencesComposable(
385425 }
386426 }
387427 }
428+ // Limit filterable extras to ones where the amount of possible values is within a reasonable limit
429+ val filterableExtraCountLimit = 5000
430+ val allExtraValuesByKey = allCodeReferencesForStat.flatMap {
431+ it.codeReference.extras.entries.toList()
432+ }.groupBy { it.key }.mapValues { it.value.map { entry -> entry.value }.toSet().sorted() }
433+ val filterableExtras = currentStatMetadata.extras.filter {
434+ val extraValues = allExtraValuesByKey[it.key] ? : emptyList()
435+ (it.type == ExtraDataType .STRING || it.type == ExtraDataType .BOOLEAN ) &&
436+ extraValues.size < filterableExtraCountLimit
437+ }.sortedBy { it.description }
438+ if (filterableExtras.isNotEmpty()) {
439+ filterableExtras.chunked(size = 2 ).map { extraGroup ->
440+ BootstrapRow {
441+ extraGroup.forEach { extra ->
442+ BootstrapColumn (6 ) {
443+ H5 {
444+ Text (extra.description)
445+ BootstrapSelectDropdown (
446+ placeholderText = " -- All Values --" ,
447+ currentValue = codeReferencesNavRoute.extras?.get(extra.key),
448+ options = allCodeReferencesForStat.mapNotNull {
449+ it.codeReference.extras[extra.key]
450+ }.toSet().map {
451+ BootstrapSelectOption (
452+ value = it,
453+ displayText = it,
454+ )
455+ }.sortedBy { it.displayText }
456+ ) {
457+ val value = it?.value
458+ var newExtras: Map <String , String >? = null
459+ val currentExtras = codeReferencesNavRoute.extras ? : mapOf ()
460+ if (value != null ) {
461+ newExtras = currentExtras + mapOf (extra.key to value)
462+ } else {
463+ newExtras = currentExtras.minus(extra.key)
464+ if (newExtras.isEmpty()) {
465+ newExtras = null
466+ }
467+ }
468+ navRouteRepo.pushNavRoute(
469+ codeReferencesNavRoute.copy(
470+ extras = newExtras
471+ )
472+ )
473+ }
474+ }
475+ }
476+ }
477+ }
478+ }
479+ }
388480
389481 BootstrapTable (
390482 headers = listOf (
0 commit comments