# HG changeset patch # User Mike Becker # Date 1738512498 -3600 # Node ID 3720c7375146c576c5bf7fb712f7c58e42a43dc4 # Parent c676c200534d94ecc7231cb736e3a8eefd40911d implement changing and saving variant status relates to #491 diff -r c676c200534d -r 3720c7375146 src/main/kotlin/de/uapcore/lightpit/RequestMapping.kt --- a/src/main/kotlin/de/uapcore/lightpit/RequestMapping.kt Sun Feb 02 14:12:02 2025 +0100 +++ b/src/main/kotlin/de/uapcore/lightpit/RequestMapping.kt Sun Feb 02 17:08:18 2025 +0100 @@ -156,6 +156,16 @@ private fun String.withExt(ext: String) = if (endsWith(ext)) this else plus(ext) private fun jspPath(name: String) = Constants.JSP_PATH_PREFIX.plus(name).withExt(".jsp") + fun paramIndexed(prefix: String): Map = buildMap { + for (name in request.parameterNames) { + if (name.startsWith(prefix)) { + val key = name.substring(prefix.length).toIntOrNull() + if (key != null) { + put(key, request.getParameter(name)) + } + } + } + } fun param(name: String): String? = request.getParameter(name) fun paramArray(name: String): Array = request.getParameterValues(name) ?: emptyArray() diff -r c676c200534d -r 3720c7375146 src/main/kotlin/de/uapcore/lightpit/dao/PostgresDataAccessObject.kt --- a/src/main/kotlin/de/uapcore/lightpit/dao/PostgresDataAccessObject.kt Sun Feb 02 14:12:02 2025 +0100 +++ b/src/main/kotlin/de/uapcore/lightpit/dao/PostgresDataAccessObject.kt Sun Feb 02 17:08:18 2025 +0100 @@ -879,7 +879,7 @@ """ insert into lpit_issue_variant_status (issueid, variant, status) values (?, ?, ?::issue_status) - on conflict do update + on conflict (issueid, variant) do update set status = ?::issue_status, outdated = false """.trimIndent() ) { diff -r c676c200534d -r 3720c7375146 src/main/kotlin/de/uapcore/lightpit/entities/Issue.kt --- a/src/main/kotlin/de/uapcore/lightpit/entities/Issue.kt Sun Feb 02 14:12:02 2025 +0100 +++ b/src/main/kotlin/de/uapcore/lightpit/entities/Issue.kt Sun Feb 02 17:08:18 2025 +0100 @@ -55,6 +55,9 @@ fun removeVariant(variant: Variant) { variantStatus.remove(variant) } + fun removeAllVariants() { + variantStatus.clear() + } fun getStatusForVariant(variant: Variant): IssueStatus? { return variantStatus[variant] } diff -r c676c200534d -r 3720c7375146 src/main/kotlin/de/uapcore/lightpit/logic/IssueLogic.kt --- a/src/main/kotlin/de/uapcore/lightpit/logic/IssueLogic.kt Sun Feb 02 14:12:02 2025 +0100 +++ b/src/main/kotlin/de/uapcore/lightpit/logic/IssueLogic.kt Sun Feb 02 17:08:18 2025 +0100 @@ -3,9 +3,7 @@ import de.uapcore.lightpit.HttpRequest import de.uapcore.lightpit.dao.DataAccessObject import de.uapcore.lightpit.dateOptValidator -import de.uapcore.lightpit.entities.Issue -import de.uapcore.lightpit.entities.IssueComment -import de.uapcore.lightpit.entities.IssueRelation +import de.uapcore.lightpit.entities.* import de.uapcore.lightpit.types.IssueCategory import de.uapcore.lightpit.types.IssueStatus import de.uapcore.lightpit.types.RelationType @@ -15,7 +13,10 @@ import de.uapcore.lightpit.viewmodel.projectNavMenu import java.sql.Date -fun Issue.hasChanged(reference: Issue) = !(component == reference.component && +fun Issue.hasChanged(reference: Issue) = + (isTrackingVariantStatus xor reference.isTrackingVariantStatus) + || (isTrackingVariantStatus && !variantStatus.equals(reference)) + || !(component == reference.component && status == reference.status && category == reference.category && subject == reference.subject && @@ -33,7 +34,10 @@ else eta.compareTo(date) } -fun Issue.applyFormData(http: HttpRequest, dao: DataAccessObject): Issue = this.apply { +fun Issue.applyFormData( + http: HttpRequest, dao: DataAccessObject, + versions: List? = null, variants: List? = null, +): Issue = this.apply { component = dao.findComponent(http.param("component")?.toIntOrNull() ?: -1) category = IssueCategory.valueOf(http.param("category") ?: "") status = IssueStatus.valueOf(http.param("status") ?: "") @@ -49,10 +53,32 @@ // TODO: process error messages eta = http.param("eta", ::dateOptValidator, null, mutableListOf()) + // if versions are selected, but we don't have a version cache, request the dao + val versionsLookup = versions + ?: if (http.param("affected") != null || http.param("resolved") != null) + dao.listVersions(project) else emptyList() + // we must resolve the versions with the DAO, otherwise we do not get the names for the history - val vlookup = {paramKey: String -> http.param(paramKey)?.toIntOrNull()?.takeIf { it > 0 }?.let { dao.findVersion(it) }} + val vlookup = {paramKey: String -> http.param(paramKey)?.toIntOrNull()?.takeIf { it > 0 }?.let {p -> versionsLookup.find { v -> v.id == p } }} affected = vlookup("affected") resolved = vlookup("resolved") + + // process possible issue variants + if (http.param("use-variants") == null) { + removeAllVariants() + } else { + val variantsLookup = variants ?: dao.listVariants(project) + http.paramIndexed("status-variant-").forEach { (variantid, status) -> + variantsLookup.find { v -> v.id == variantid }?.let { variant -> + if (status == "not-relevant") { + removeVariant(variant) + } else { + setStatusForVariant(variant, IssueStatus.valueOf(status)) + } + } + } + // TODO: compute overall status + } } fun processIssueForm(issue: Issue, reference: Issue, http: HttpRequest, dao: DataAccessObject) { diff -r c676c200534d -r 3720c7375146 src/main/kotlin/de/uapcore/lightpit/servlet/IssuesServlet.kt --- a/src/main/kotlin/de/uapcore/lightpit/servlet/IssuesServlet.kt Sun Feb 02 14:12:02 2025 +0100 +++ b/src/main/kotlin/de/uapcore/lightpit/servlet/IssuesServlet.kt Sun Feb 02 17:08:18 2025 +0100 @@ -71,6 +71,7 @@ issue, dao.listVersions(issue.project), dao.listComponents(issue.project), + dao.listVariants(issue.project), dao.listUsers(), issue.project ) diff -r c676c200534d -r 3720c7375146 src/main/kotlin/de/uapcore/lightpit/servlet/ProjectServlet.kt --- a/src/main/kotlin/de/uapcore/lightpit/servlet/ProjectServlet.kt Sun Feb 02 14:12:02 2025 +0100 +++ b/src/main/kotlin/de/uapcore/lightpit/servlet/ProjectServlet.kt Sun Feb 02 17:08:18 2025 +0100 @@ -434,6 +434,7 @@ issue, path.projectInfo.versions, path.projectInfo.components, + path.projectInfo.variants, dao.listUsers(), path.projectInfo.project, path @@ -457,7 +458,7 @@ val issue = Issue( http.param("id")?.toIntOrNull() ?: -1, project - ).applyFormData(http, dao) + ).applyFormData(http, dao, projectInfo.versions, projectInfo.variants) val openId = if (issue.id < 0) { val remoteUser = http.remoteUser?.let { dao.findUserByName(it) } diff -r c676c200534d -r 3720c7375146 src/main/kotlin/de/uapcore/lightpit/viewmodel/Issues.kt --- a/src/main/kotlin/de/uapcore/lightpit/viewmodel/Issues.kt Sun Feb 02 14:12:02 2025 +0100 +++ b/src/main/kotlin/de/uapcore/lightpit/viewmodel/Issues.kt Sun Feb 02 17:08:18 2025 +0100 @@ -178,6 +178,7 @@ val issue: Issue, val versions: List, val components: List, + val variants: List, val users: List, val project: Project, val pathInfos: PathInfos? = null diff -r c676c200534d -r 3720c7375146 src/main/resources/localization/strings.properties --- a/src/main/resources/localization/strings.properties Sun Feb 02 14:12:02 2025 +0100 +++ b/src/main/resources/localization/strings.properties Sun Feb 02 17:08:18 2025 +0100 @@ -128,6 +128,9 @@ issue.status=Status issue.subject=Subject issue.updated=Updated +issue.variants=Variants +issue.variants.checkbox-text=Use individual status for each variant +issue.variants.not-relevant=Not Relevant issues.active=In Progress issues.done=Done issues.open=Open diff -r c676c200534d -r 3720c7375146 src/main/resources/localization/strings_de.properties --- a/src/main/resources/localization/strings_de.properties Sun Feb 02 14:12:02 2025 +0100 +++ b/src/main/resources/localization/strings_de.properties Sun Feb 02 17:08:18 2025 +0100 @@ -128,6 +128,9 @@ issue.status=Status issue.subject=Thema issue.updated=Aktualisiert +issue.variants=Varianten +issue.variants.checkbox-text=Individueller Status f\u00fcr jede Variante +issue.variants.not-relevant=Nicht Relevant issues.active=In Arbeit issues.done=Erledigt issues.open=Offen diff -r c676c200534d -r 3720c7375146 src/main/webapp/WEB-INF/jsp/issue-form.jsp --- a/src/main/webapp/WEB-INF/jsp/issue-form.jsp Sun Feb 02 14:12:02 2025 +0100 +++ b/src/main/webapp/WEB-INF/jsp/issue-form.jsp Sun Feb 02 17:08:18 2025 +0100 @@ -81,6 +81,20 @@ + + + + + checked + onclick="toggleVariantStatus()" + /> + + + + @@ -93,6 +107,29 @@ +
+ +
"> + + +
+
+
diff -r c676c200534d -r 3720c7375146 src/main/webapp/WEB-INF/jsp/site.jsp --- a/src/main/webapp/WEB-INF/jsp/site.jsp Sun Feb 02 14:12:02 2025 +0100 +++ b/src/main/webapp/WEB-INF/jsp/site.jsp Sun Feb 02 17:08:18 2025 +0100 @@ -31,7 +31,7 @@ <%@taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %> <%-- Version suffix for forcing browsers to update the CSS / JS files --%> - + <%-- Make the base href easily available at request scope --%> diff -r c676c200534d -r 3720c7375146 src/main/webapp/issue-editor.js --- a/src/main/webapp/issue-editor.js Sun Feb 02 14:12:02 2025 +0100 +++ b/src/main/webapp/issue-editor.js Sun Feb 02 17:08:18 2025 +0100 @@ -41,3 +41,19 @@ editor.style.display='none' view.style.display='block' } + +function toggleVariantStatus() { + const cbox = document.getElementById('use-variants') + if (!cbox) return + const issue_status = document.getElementById('issue-status') + const variant_status = document.getElementById('issue-variant-status') + if (cbox.checked) { + issue_status.style.display = 'none' + variant_status.style.display = 'flex' + } else { + issue_status.style.display = 'inline-block' + variant_status.style.display = 'none' + } +} + +window.addEventListener("load", (_) => toggleVariantStatus()); diff -r c676c200534d -r 3720c7375146 src/main/webapp/projects.css --- a/src/main/webapp/projects.css Sun Feb 02 14:12:02 2025 +0100 +++ b/src/main/webapp/projects.css Sun Feb 02 17:08:18 2025 +0100 @@ -193,6 +193,12 @@ white-space: nowrap; } +#issue-variant-status { + display: flex; + gap: 1em; + flex-wrap: wrap; +} + table.relation-editor input, table.relation-editor button, table.relation-editor .button {