From ae7898747eda6f49c8aaa074bfa7eb53721e0b0a Mon Sep 17 00:00:00 2001 From: "Kofi K." Date: Tue, 24 Feb 2026 02:05:30 +0100 Subject: [PATCH] filter models page by application area --- .../lux/sections/luxmodels/LuxModels.kt | 85 ++++++++----------- .../luxmodels/components/ModelAccessFilter.kt | 42 +++++++++ .../luxmodels/components/ModelAreaFilter.kt | 30 +++++++ .../luxmodels/components/ModelFilter.kt | 46 ++++++++++ .../components/filterAndSearchModels.kt | 12 ++- .../domains/lux/widgets/MultiSelectRow.kt | 42 +++++++++ 6 files changed, 203 insertions(+), 54 deletions(-) create mode 100644 site/src/jsMain/kotlin/com/zenmo/web/zenmo/domains/lux/sections/luxmodels/components/ModelAccessFilter.kt create mode 100644 site/src/jsMain/kotlin/com/zenmo/web/zenmo/domains/lux/sections/luxmodels/components/ModelAreaFilter.kt create mode 100644 site/src/jsMain/kotlin/com/zenmo/web/zenmo/domains/lux/sections/luxmodels/components/ModelFilter.kt create mode 100644 site/src/jsMain/kotlin/com/zenmo/web/zenmo/domains/lux/widgets/MultiSelectRow.kt diff --git a/site/src/jsMain/kotlin/com/zenmo/web/zenmo/domains/lux/sections/luxmodels/LuxModels.kt b/site/src/jsMain/kotlin/com/zenmo/web/zenmo/domains/lux/sections/luxmodels/LuxModels.kt index 67dc5c8e..4a251e1b 100644 --- a/site/src/jsMain/kotlin/com/zenmo/web/zenmo/domains/lux/sections/luxmodels/LuxModels.kt +++ b/site/src/jsMain/kotlin/com/zenmo/web/zenmo/domains/lux/sections/luxmodels/LuxModels.kt @@ -1,34 +1,29 @@ package com.zenmo.web.zenmo.domains.lux.sections.luxmodels import androidx.compose.runtime.* -import com.varabyte.kobweb.compose.css.TextTransform import com.varabyte.kobweb.compose.foundation.layout.Arrangement import com.varabyte.kobweb.compose.foundation.layout.Column -import com.varabyte.kobweb.compose.foundation.layout.Row import com.varabyte.kobweb.compose.ui.Alignment import com.varabyte.kobweb.compose.ui.Modifier -import com.varabyte.kobweb.compose.ui.modifiers.* +import com.varabyte.kobweb.compose.ui.modifiers.background +import com.varabyte.kobweb.compose.ui.modifiers.gap +import com.varabyte.kobweb.compose.ui.modifiers.margin import com.varabyte.kobweb.compose.ui.toAttrs -import com.varabyte.kobweb.silk.components.icons.mdi.MdiLock import com.varabyte.kobweb.silk.style.toModifier import com.zenmo.web.zenmo.components.widgets.LangText -import com.zenmo.web.zenmo.components.widgets.SectionContainer +import com.zenmo.web.zenmo.domains.lux.components.LuxSectionContainer import com.zenmo.web.zenmo.domains.lux.core.model.subdomain.subdomainModels import com.zenmo.web.zenmo.domains.lux.core.toTwinModelCardItem -import com.zenmo.web.zenmo.domains.lux.sections.DeEmphasizedTextStyle -import com.zenmo.web.zenmo.domains.lux.sections.LuxSectionContainerStyleVariant -import com.zenmo.web.zenmo.domains.lux.sections.luxmodels.components.EmptyResults -import com.zenmo.web.zenmo.domains.lux.sections.luxmodels.components.SearchBar -import com.zenmo.web.zenmo.domains.lux.sections.luxmodels.components.filterAndSearchModels -import com.zenmo.web.zenmo.domains.lux.widgets.RadioRow +import com.zenmo.web.zenmo.domains.lux.sections.ResponsiveFlexStyle +import com.zenmo.web.zenmo.domains.lux.sections.application_fields.LuxApplicationArea +import com.zenmo.web.zenmo.domains.lux.sections.luxmodels.components.* import com.zenmo.web.zenmo.domains.lux.widgets.TwinModelsGrid import com.zenmo.web.zenmo.domains.lux.widgets.headings.HeaderText import com.zenmo.web.zenmo.theme.SitePalette -import org.jetbrains.compose.web.css.Position import org.jetbrains.compose.web.css.cssRem import org.jetbrains.compose.web.css.px +import org.jetbrains.compose.web.dom.Div import org.jetbrains.compose.web.dom.P -import org.jetbrains.compose.web.dom.Span enum class FilterType { @@ -40,17 +35,13 @@ enum class FilterType { @Composable fun LuxModels() { - SectionContainer( - horizontalAlignment = Alignment.CenterHorizontally, - verticalArrangement = Arrangement.Center, + LuxSectionContainer( modifier = Modifier .background(SitePalette.light.overlay) - .position(Position.Relative) - .gap(5.cssRem), - variant = LuxSectionContainerStyleVariant ) { var query by remember { mutableStateOf("") } + var selectedAreaOptions by remember { mutableStateOf(emptySet()) } val allModels = (subdomainModels + com.zenmo.web.zenmo.domains.lux.subdomains.private_subdomains.drechtsteden.drechtstedenModels).map { it.toTwinModelCardItem() } @@ -82,49 +73,41 @@ fun LuxModels() { luxModels = filterAndSearchModels( models = allModels, query = query, - filterType = filterType + filterType = filterType, + areas = selectedAreaOptions ) } ) - Column( - horizontalAlignment = Alignment.CenterHorizontally, + Div( + ResponsiveFlexStyle.toModifier() + .gap(0.5.cssRem) + .toAttrs() ) { - Span( - attrs = DeEmphasizedTextStyle.toModifier() - .textTransform(TextTransform.Uppercase) - .padding(bottom = 8.px) - .toAttrs() - ) { - LangText( - en = "Access", - nl = "Toegang" - ) - } - RadioRow( - value = filterType, - options = FilterType.entries.associateWith { it.name }, - onChange = { type -> + ModelAccessFilter( + filterType = filterType, + onFilterChange = { type -> filterType = type luxModels = filterAndSearchModels( models = allModels, query = query, - filterType = filterType + filterType = filterType, + areas = selectedAreaOptions ) } - ) { option, _ -> - when (option) { - FilterType.ALL -> LangText(en = "All", nl = "Alle") - FilterType.PUBLIC -> LangText(en = "Public", nl = "Openbaar") - FilterType.PRIVATE -> Row( - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.spacedBy(4.px) - ) { - MdiLock(Modifier.fontSize(16.px)) - LangText(en = "Private", nl = "Privé") - } + ) + ModelAreaFilter( + selectedOptions = selectedAreaOptions, + onSelectionChange = { + selectedAreaOptions = it + luxModels = filterAndSearchModels( + models = allModels, + query = query, + filterType = filterType, + areas = selectedAreaOptions + ) } - } + ) } } @@ -136,4 +119,4 @@ fun LuxModels() { EmptyResults() } } -} +} \ No newline at end of file diff --git a/site/src/jsMain/kotlin/com/zenmo/web/zenmo/domains/lux/sections/luxmodels/components/ModelAccessFilter.kt b/site/src/jsMain/kotlin/com/zenmo/web/zenmo/domains/lux/sections/luxmodels/components/ModelAccessFilter.kt new file mode 100644 index 00000000..cbf4599d --- /dev/null +++ b/site/src/jsMain/kotlin/com/zenmo/web/zenmo/domains/lux/sections/luxmodels/components/ModelAccessFilter.kt @@ -0,0 +1,42 @@ +package com.zenmo.web.zenmo.domains.lux.sections.luxmodels.components + +import androidx.compose.runtime.Composable +import com.varabyte.kobweb.compose.foundation.layout.Arrangement +import com.varabyte.kobweb.compose.foundation.layout.Row +import com.varabyte.kobweb.compose.ui.Alignment +import com.varabyte.kobweb.compose.ui.Modifier +import com.varabyte.kobweb.compose.ui.modifiers.fontSize +import com.varabyte.kobweb.silk.components.icons.mdi.MdiLock +import com.zenmo.web.zenmo.components.widgets.LangText +import com.zenmo.web.zenmo.core.services.localization.LocalizedText +import com.zenmo.web.zenmo.domains.lux.sections.luxmodels.FilterType +import com.zenmo.web.zenmo.domains.lux.widgets.RadioRow +import org.jetbrains.compose.web.css.px + +@Composable +fun ModelAccessFilter( + filterType: FilterType, + onFilterChange: (FilterType) -> Unit +) { + ModelFilter( + filterLabel = LocalizedText(en = "Access", nl = "Toegang") + ) { + RadioRow( + value = filterType, + options = FilterType.entries.associateWith { it.name }, + onChange = onFilterChange + ) { option, _ -> + when (option) { + FilterType.ALL -> LangText(en = "All", nl = "Alle") + FilterType.PUBLIC -> LangText(en = "Public", nl = "Openbaar") + FilterType.PRIVATE -> Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(4.px) + ) { + MdiLock(Modifier.fontSize(16.px)) + LangText(en = "Private", nl = "Privé") + } + } + } + } +} \ No newline at end of file diff --git a/site/src/jsMain/kotlin/com/zenmo/web/zenmo/domains/lux/sections/luxmodels/components/ModelAreaFilter.kt b/site/src/jsMain/kotlin/com/zenmo/web/zenmo/domains/lux/sections/luxmodels/components/ModelAreaFilter.kt new file mode 100644 index 00000000..1ee5823c --- /dev/null +++ b/site/src/jsMain/kotlin/com/zenmo/web/zenmo/domains/lux/sections/luxmodels/components/ModelAreaFilter.kt @@ -0,0 +1,30 @@ +package com.zenmo.web.zenmo.domains.lux.sections.luxmodels.components + +import androidx.compose.runtime.Composable +import com.zenmo.web.zenmo.core.services.localization.Language +import com.zenmo.web.zenmo.core.services.localization.LocalLanguage +import com.zenmo.web.zenmo.core.services.localization.LocalizedText +import com.zenmo.web.zenmo.domains.lux.sections.application_fields.LuxApplicationArea +import com.zenmo.web.zenmo.domains.lux.widgets.MultiSelectRow + +@Composable +fun ModelAreaFilter( + selectedOptions: Set, + onSelectionChange: (Set) -> Unit, +) { + val currentLanguage = LocalLanguage.current + ModelFilter( + filterLabel = LocalizedText(en = "Application Area", nl = "Toepassingsgebied") + ) { + MultiSelectRow( + selectedValues = selectedOptions, + options = LuxApplicationArea.entries.associateWith { + when (currentLanguage) { + Language.Dutch -> it.label.nl + Language.English -> it.label.en + } + }, + onChange = onSelectionChange + ) + } +} \ No newline at end of file diff --git a/site/src/jsMain/kotlin/com/zenmo/web/zenmo/domains/lux/sections/luxmodels/components/ModelFilter.kt b/site/src/jsMain/kotlin/com/zenmo/web/zenmo/domains/lux/sections/luxmodels/components/ModelFilter.kt new file mode 100644 index 00000000..2fd459cb --- /dev/null +++ b/site/src/jsMain/kotlin/com/zenmo/web/zenmo/domains/lux/sections/luxmodels/components/ModelFilter.kt @@ -0,0 +1,46 @@ +package com.zenmo.web.zenmo.domains.lux.sections.luxmodels.components + +import androidx.compose.runtime.Composable +import com.varabyte.kobweb.compose.css.TextTransform +import com.varabyte.kobweb.compose.foundation.layout.Column +import com.varabyte.kobweb.compose.ui.Alignment +import com.varabyte.kobweb.compose.ui.modifiers.gap +import com.varabyte.kobweb.compose.ui.modifiers.padding +import com.varabyte.kobweb.compose.ui.modifiers.textTransform +import com.varabyte.kobweb.compose.ui.toAttrs +import com.varabyte.kobweb.silk.style.toModifier +import com.zenmo.web.zenmo.components.widgets.LangText +import com.zenmo.web.zenmo.core.services.localization.LocalizedText +import com.zenmo.web.zenmo.domains.lux.sections.DeEmphasizedTextStyle +import com.zenmo.web.zenmo.domains.lux.sections.ResponsiveFlexStyle +import org.jetbrains.compose.web.css.cssRem +import org.jetbrains.compose.web.css.px +import org.jetbrains.compose.web.dom.Div +import org.jetbrains.compose.web.dom.Span + +@Composable +fun ModelFilter( + filterLabel: LocalizedText, + content: @Composable () -> Unit, +) { + Column( + horizontalAlignment = Alignment.CenterHorizontally, + ) { + Span( + attrs = DeEmphasizedTextStyle.toModifier() + .textTransform(TextTransform.Uppercase) + .padding(bottom = 8.px) + .toAttrs() + ) { + LangText( + en = filterLabel.en, + nl = filterLabel.nl + ) + } + Div( + ResponsiveFlexStyle.toModifier() + .gap(0.5.cssRem) + .toAttrs() + ) { content() } + } +} \ No newline at end of file diff --git a/site/src/jsMain/kotlin/com/zenmo/web/zenmo/domains/lux/sections/luxmodels/components/filterAndSearchModels.kt b/site/src/jsMain/kotlin/com/zenmo/web/zenmo/domains/lux/sections/luxmodels/components/filterAndSearchModels.kt index 5775566e..1847724e 100644 --- a/site/src/jsMain/kotlin/com/zenmo/web/zenmo/domains/lux/sections/luxmodels/components/filterAndSearchModels.kt +++ b/site/src/jsMain/kotlin/com/zenmo/web/zenmo/domains/lux/sections/luxmodels/components/filterAndSearchModels.kt @@ -1,20 +1,22 @@ package com.zenmo.web.zenmo.domains.lux.sections.luxmodels.components import com.zenmo.web.zenmo.domains.lux.core.TwinModelCardItem +import com.zenmo.web.zenmo.domains.lux.sections.application_fields.LuxApplicationArea import com.zenmo.web.zenmo.domains.lux.sections.luxmodels.FilterType /** - * Filters and searches a list of models based on visibility and title. + * Filters and searches a list of models based on visibility, matching title, and application area. * - * Applies the selected [filterType] and + * Applies the selected [filterType], filters by [areas] if specified, and * optionally narrows the result by matching the [query] against * each model’s title (case-insensitive). */ fun filterAndSearchModels( models: List, query: String, - filterType: FilterType + filterType: FilterType, + areas: Set ): List { val queryString = query.trim().lowercase() @@ -34,5 +36,9 @@ fun filterAndSearchModels( sTokens.lowercase().contains(queryString) } } + .filter { model -> + if (areas.isEmpty()) return@filter true + model.applicationArea in areas + } .toList() } \ No newline at end of file diff --git a/site/src/jsMain/kotlin/com/zenmo/web/zenmo/domains/lux/widgets/MultiSelectRow.kt b/site/src/jsMain/kotlin/com/zenmo/web/zenmo/domains/lux/widgets/MultiSelectRow.kt new file mode 100644 index 00000000..785281a3 --- /dev/null +++ b/site/src/jsMain/kotlin/com/zenmo/web/zenmo/domains/lux/widgets/MultiSelectRow.kt @@ -0,0 +1,42 @@ +package com.zenmo.web.zenmo.domains.lux.widgets + +import androidx.compose.runtime.Composable +import com.varabyte.kobweb.compose.foundation.layout.Arrangement +import com.varabyte.kobweb.compose.foundation.layout.Row +import com.varabyte.kobweb.compose.ui.Alignment +import com.varabyte.kobweb.compose.ui.Modifier +import com.varabyte.kobweb.compose.ui.modifiers.gap +import org.jetbrains.compose.web.css.cssRem +import org.jetbrains.compose.web.dom.Text + +@Composable +fun MultiSelectRow( + selectedValues: Set, + options: Map, + onChange: (Set) -> Unit, + modifier: Modifier = Modifier.Companion, +) { + Row( + horizontalArrangement = Arrangement.Center, + verticalAlignment = Alignment.CenterVertically, + modifier = modifier.gap(0.5.cssRem) + ) { + options.forEach { (optionValue, displayName) -> + val isSelected = optionValue in selectedValues + RadioItem( + onClick = { + val selection = selectedValues.toMutableSet() + if (isSelected) { + selection.remove(optionValue) + } else { + selection.add(optionValue) + } + onChange(selection) + }, + isSelected = isSelected, + ) { + Text(displayName) + } + } + } +} \ No newline at end of file