diff --git a/sormas-api/src/main/java/de/symeda/sormas/api/externalmessage/ExternalMessageAdapterFacade.java b/sormas-api/src/main/java/de/symeda/sormas/api/externalmessage/ExternalMessageAdapterFacade.java
index 741000e2794..b6ab8d4d659 100644
--- a/sormas-api/src/main/java/de/symeda/sormas/api/externalmessage/ExternalMessageAdapterFacade.java
+++ b/sormas-api/src/main/java/de/symeda/sormas/api/externalmessage/ExternalMessageAdapterFacade.java
@@ -1,3 +1,18 @@
+/*
+ * SORMAS® - Surveillance Outbreak Response Management & Analysis System
+ * Copyright © 2016-2026 SORMAS Foundation gGmbH
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
package de.symeda.sormas.api.externalmessage;
import java.util.Date;
@@ -16,6 +31,7 @@ public interface ExternalMessageAdapterFacade {
ExternalMessageResult> getExternalMessages(Date since);
/**
+ * The method should return the HTML source to display a structured/simpler view of the message.
*
* @param message
* message to be converted
@@ -24,6 +40,18 @@ public interface ExternalMessageAdapterFacade {
*/
ExternalMessageResult convertToHTML(ExternalMessageDto message);
+ /**
+ * The method should return the HTML source to display a detailed view of the message.
+ *
+ * @param message
+ * message to be converted
+ * @return An ExternalMessageResult with a String that is a full/raw HTML representation of the message,
+ * Falls back to {@link #convertToHTML(ExternalMessageDto)} by default.
+ */
+ default ExternalMessageResult convertToDetailedHTML(ExternalMessageDto message) {
+ return convertToHTML(message);
+ }
+
/**
*
* @param message
diff --git a/sormas-api/src/main/java/de/symeda/sormas/api/i18n/Captions.java b/sormas-api/src/main/java/de/symeda/sormas/api/i18n/Captions.java
index 402f110b303..a143a8dc941 100644
--- a/sormas-api/src/main/java/de/symeda/sormas/api/i18n/Captions.java
+++ b/sormas-api/src/main/java/de/symeda/sormas/api/i18n/Captions.java
@@ -1979,6 +1979,7 @@ public interface Captions {
String externalMessage_deleteNewlyCreatedCase = "externalMessage.deleteNewlyCreatedCase";
String externalMessage_deleteNewlyCreatedContact = "externalMessage.deleteNewlyCreatedContact";
String externalMessage_deleteNewlyCreatedEventParticipant = "externalMessage.deleteNewlyCreatedEventParticipant";
+ String externalMessage_detailedView = "externalMessage.detailedView";
String ExternalMessage_disease = "ExternalMessage.disease";
String ExternalMessage_diseaseVariant = "ExternalMessage.diseaseVariant";
String ExternalMessage_diseaseVariantDetails = "ExternalMessage.diseaseVariantDetails";
@@ -2012,6 +2013,7 @@ public interface Captions {
String ExternalMessage_sampleReceivedDate = "ExternalMessage.sampleReceivedDate";
String ExternalMessage_specimenCondition = "ExternalMessage.specimenCondition";
String ExternalMessage_status = "ExternalMessage.status";
+ String externalMessage_structuredView = "externalMessage.structuredView";
String ExternalMessage_surveillanceReport = "ExternalMessage.surveillanceReport";
String ExternalMessage_type = "ExternalMessage.type";
String ExternalMessageCriteria_birthDateFrom = "ExternalMessageCriteria.birthDateFrom";
diff --git a/sormas-api/src/main/resources/captions.properties b/sormas-api/src/main/resources/captions.properties
index 9ee5c7030f8..e00f16a8c9c 100644
--- a/sormas-api/src/main/resources/captions.properties
+++ b/sormas-api/src/main/resources/captions.properties
@@ -1716,6 +1716,8 @@ infrastructureImportAllowOverwrite=Overwrite existing entries with imported data
#External Message
ExternalMessage=Message
ExternalMessage.labMessageDetails=Message details
+externalMessage.structuredView=Structured view
+externalMessage.detailedView=Detailed view
ExternalMessage.labSampleId=Lab sample ID
ExternalMessage.messageDateTime=Message date
ExternalMessage.personBirthDateDD=Day of birth
diff --git a/sormas-ui/src/main/java/de/symeda/sormas/ui/externalmessage/ExternalMessageForm.java b/sormas-ui/src/main/java/de/symeda/sormas/ui/externalmessage/ExternalMessageForm.java
index 8e17999ab70..06d6e5e93f8 100644
--- a/sormas-ui/src/main/java/de/symeda/sormas/ui/externalmessage/ExternalMessageForm.java
+++ b/sormas-ui/src/main/java/de/symeda/sormas/ui/externalmessage/ExternalMessageForm.java
@@ -1,3 +1,18 @@
+/*
+ * SORMAS® - Surveillance Outbreak Response Management & Analysis System
+ * Copyright © 2016-2026 SORMAS Foundation gGmbH
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
package de.symeda.sormas.ui.externalmessage;
import static de.symeda.sormas.ui.utils.LayoutUtil.fluidRowLocs;
@@ -10,26 +25,41 @@
import com.vaadin.ui.CustomLayout;
import com.vaadin.ui.Label;
import com.vaadin.ui.Panel;
-import com.vaadin.v7.data.util.converter.Converter;
+import com.vaadin.ui.TabSheet;
+import com.vaadin.ui.VerticalLayout;
import de.symeda.sormas.api.FacadeProvider;
import de.symeda.sormas.api.externalmessage.ExternalMessageDto;
import de.symeda.sormas.api.externalmessage.ExternalMessageResult;
+import de.symeda.sormas.api.i18n.Captions;
+import de.symeda.sormas.api.i18n.I18nProperties;
import de.symeda.sormas.ui.utils.AbstractEditForm;
import de.symeda.sormas.ui.utils.VaadinUiUtil;
+@SuppressWarnings({
+ "java:S110", // suppress sonar too many parents warning
+ "java:S2160" // equals not overridden; value-equality is not needed for Vaadin form components
+})
public class ExternalMessageForm extends AbstractEditForm {
private static final long serialVersionUID = -3859401780981133265L;
//@formatter:off
private static final String HTML_LAYOUT =
- fluidRowLocs(ExternalMessageDto.UUID, ExternalMessageDto.MESSAGE_DATE_TIME) +
+ fluidRowLocs(ExternalMessageDto.UUID, ExternalMessageDto.MESSAGE_DATE_TIME) +
fluidRowLocs(ExternalMessageDto.EXTERNAL_MESSAGE_DETAILS);
//@formatter:on
- private Panel detailsPanel;
+ private TabSheet detailsTabSheet;
+ private Panel structuredViewPanel;
+ @SuppressWarnings("java:S1450") // referenced in addFields, tab listener and setValue - cannot be local
+ private Panel detailedViewPanel;
+ private VerticalLayout structuredViewContainer;
+ private VerticalLayout detailedViewContainer;
+ private boolean structuredViewLoaded;
+ private boolean detailedViewLoaded;
+ @SuppressWarnings("java:S1948") // Logger is effectively serializable via its name
private final Logger logger = LoggerFactory.getLogger(getClass());
public ExternalMessageForm() {
@@ -40,10 +70,44 @@ public ExternalMessageForm() {
protected void addFields() {
addFields(ExternalMessageDto.UUID, ExternalMessageDto.MESSAGE_DATE_TIME);
- detailsPanel = new Panel();
- detailsPanel.setHeightFull();
- detailsPanel.addStyleName("lab-message-details");
- getContent().addComponent(detailsPanel, ExternalMessageDto.EXTERNAL_MESSAGE_DETAILS);
+ structuredViewContainer = new VerticalLayout();
+ structuredViewContainer.setMargin(false);
+ structuredViewContainer.setSizeUndefined();
+
+ structuredViewPanel = new Panel();
+ structuredViewPanel.setSizeFull();
+ structuredViewPanel.addStyleName("lab-message-structured-view");
+ structuredViewPanel.setContent(structuredViewContainer);
+
+ detailedViewContainer = new VerticalLayout();
+ detailedViewContainer.setMargin(false);
+ detailedViewContainer.setSizeUndefined();
+
+ detailedViewPanel = new Panel();
+ detailedViewPanel.setSizeFull();
+ detailedViewPanel.addStyleName("lab-message-detailed-view");
+ detailedViewPanel.setContent(detailedViewContainer);
+
+ detailsTabSheet = new TabSheet();
+ detailsTabSheet.setSizeFull();
+ detailsTabSheet.addStyleName("lab-message-details");
+ detailsTabSheet.addTab(structuredViewPanel, I18nProperties.getCaption(Captions.externalMessage_structuredView));
+ detailsTabSheet.addTab(detailedViewPanel, I18nProperties.getCaption(Captions.externalMessage_detailedView));
+
+ detailsTabSheet.addSelectedTabChangeListener(event -> {
+ ExternalMessageDto msg = getValue();
+ if (msg == null) {
+ return;
+ }
+ Component selected = event.getTabSheet().getSelectedTab();
+ if (selected == structuredViewPanel) {
+ loadStructuredView(msg);
+ } else if (selected == detailedViewPanel) {
+ loadDetailedView(msg);
+ }
+ });
+
+ getContent().addComponent(detailsTabSheet, ExternalMessageDto.EXTERNAL_MESSAGE_DETAILS);
}
@Override
@@ -52,23 +116,57 @@ protected String createHtmlLayout() {
}
@Override
- public void setValue(ExternalMessageDto externalMessage) throws ReadOnlyException, Converter.ConversionException {
+ public void setValue(ExternalMessageDto externalMessage) {
super.setValue(externalMessage);
getFieldGroup().setReadOnly(true);
+ structuredViewLoaded = false;
+ detailedViewLoaded = false;
+ structuredViewContainer.removeAllComponents();
+ detailedViewContainer.removeAllComponents();
+ loadStructuredView(externalMessage);
+ detailsTabSheet.setSelectedTab(structuredViewPanel);
+ }
+
+ private void loadStructuredView(ExternalMessageDto externalMessage) {
+ if (structuredViewLoaded) {
+ return;
+ }
+ try {
+ ExternalMessageResult result = FacadeProvider.getExternalLabResultsFacade().convertToHTML(externalMessage);
+ if (result.isSuccess()) {
+ CustomLayout layout = new CustomLayout();
+ layout.setTemplateContents(result.getValue());
+ structuredViewContainer.addComponent(layout);
+ } else {
+ structuredViewContainer.addComponent(createXmlDisplay(externalMessage.getExternalMessageDetails()));
+ VaadinUiUtil.showWarningPopup(result.getError());
+ }
+ } catch (Exception e) {
+ structuredViewContainer.addComponent(createXmlDisplay(externalMessage.getExternalMessageDetails()));
+ logger.error(e.getMessage());
+ }
+ structuredViewLoaded = true;
+ }
+
+ private void loadDetailedView(ExternalMessageDto externalMessage) {
+ if (detailedViewLoaded) {
+ return;
+ }
try {
- ExternalMessageResult htmlConversionResult = FacadeProvider.getExternalLabResultsFacade().convertToHTML(externalMessage);
- if (htmlConversionResult.isSuccess()) {
- CustomLayout externalMessageDetails = new CustomLayout();
- externalMessageDetails.setTemplateContents(htmlConversionResult.getValue());
- detailsPanel.setContent(externalMessageDetails);
+ ExternalMessageResult result = FacadeProvider.getExternalLabResultsFacade().convertToDetailedHTML(externalMessage);
+ if (result.isSuccess()) {
+ CustomLayout layout = new CustomLayout();
+ layout.setTemplateContents(result.getValue());
+ detailedViewContainer.addComponent(layout);
} else {
- detailsPanel.setContent(createXmlDisplay(externalMessage.getExternalMessageDetails()));
- VaadinUiUtil.showWarningPopup(htmlConversionResult.getError());
+ detailedViewContainer.addComponent(createXmlDisplay(externalMessage.getExternalMessageDetails()));
+ VaadinUiUtil.showWarningPopup(result.getError());
}
} catch (Exception e) {
- detailsPanel.setContent(createXmlDisplay(externalMessage.getExternalMessageDetails()));
+ detailedViewContainer.addComponent(createXmlDisplay(externalMessage.getExternalMessageDetails()));
logger.error(e.getMessage());
}
+ detailedViewLoaded = true;
}
private Component createXmlDisplay(String xml) {
diff --git a/sormas-ui/src/main/webapp/VAADIN/themes/sormas/views/lab-message.scss b/sormas-ui/src/main/webapp/VAADIN/themes/sormas/views/lab-message.scss
index 60f06866802..d8e6cc33c0c 100644
--- a/sormas-ui/src/main/webapp/VAADIN/themes/sormas/views/lab-message.scss
+++ b/sormas-ui/src/main/webapp/VAADIN/themes/sormas/views/lab-message.scss
@@ -5,6 +5,14 @@
user-select: text;
}
+ .lab-message-structured-view .v-customlayout {
+ min-width: 500px;
+ }
+
+ .lab-message-detailed-view .v-customlayout {
+ min-width: max-content;
+ }
+
.lab-message-processable,
.lab-message-slider {
.lab-message-details {
@@ -20,6 +28,7 @@
}
.no-required-border {
+
.v-textfield.v-required,
.v-filterselect.v-required .v-filterselect-input,
.v-customcomponent.v-required .v-datefield-textfield,