This is an automated email from the ASF dual-hosted git repository.

ljmotta pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/incubator-kie-tools.git


The following commit(s) were added to refs/heads/main by this push:
     new 4c24f11c0b1 [kie-issues#1826] Sandbox: Render selected indicator in 
the DMN Runner form output card (#2918)
4c24f11c0b1 is described below

commit 4c24f11c0b1c72315989774c7cabb074d4a18f31
Author: Jozef Marko <[email protected]>
AuthorDate: Tue Apr 8 18:38:59 2025 +0200

    [kie-issues#1826] Sandbox: Render selected indicator in the DMN Runner form 
output card (#2918)
    
    Co-authored-by: Tiago Bento <[email protected]>
---
 .../src/DmnFormPage.tsx                            |  1 +
 .../dmn-editor-envelope/src/DmnEditorFactory.tsx   | 12 ++++++---
 packages/dmn-editor-envelope/src/DmnEditorRoot.tsx |  2 ++
 .../src/NewDmnEditorEnvelopeApi.ts                 |  6 ++++-
 .../src/NewDmnEditorEnvelopeApiFactory.ts          |  9 ++++++-
 .../src/NewDmnEditorFactory.tsx                    | 27 ++++++++++++++++++--
 packages/dmn-editor/src/DmnEditor.tsx              | 11 ++++++++
 .../dmn-editor/stories/dmnEditorStoriesWrapper.tsx |  6 +++++
 packages/form-dmn/src/FormDmnOutputs.tsx           | 29 ++++++++++++++++++----
 packages/form-dmn/src/styles.scss                  |  4 +++
 packages/form-dmn/tests/FormDmnOutputs.test.tsx    |  1 +
 .../src/dmnRunner/DmnRunnerDrawerPanelContent.tsx  |  9 ++++++-
 .../online-editor/src/dmnRunner/DmnRunnerTable.tsx |  2 +-
 13 files changed, 105 insertions(+), 14 deletions(-)

diff --git a/packages/dev-deployment-dmn-form-webapp/src/DmnFormPage.tsx 
b/packages/dev-deployment-dmn-form-webapp/src/DmnFormPage.tsx
index 638ae1c02b9..0c3dd0c4483 100644
--- a/packages/dev-deployment-dmn-form-webapp/src/DmnFormPage.tsx
+++ b/packages/dev-deployment-dmn-form-webapp/src/DmnFormPage.tsx
@@ -220,6 +220,7 @@ export function DmnFormPage(props: Props) {
                       differences={formOutputDiffs}
                       locale={locale}
                       notificationsPanel={false}
+                      openedBoxedExpressionEditorNodeId={undefined}
                     />
                   </PageSection>
                 </div>
diff --git a/packages/dmn-editor-envelope/src/DmnEditorFactory.tsx 
b/packages/dmn-editor-envelope/src/DmnEditorFactory.tsx
index c89de8179f9..3b024375cf2 100644
--- a/packages/dmn-editor-envelope/src/DmnEditorFactory.tsx
+++ b/packages/dmn-editor-envelope/src/DmnEditorFactory.tsx
@@ -49,8 +49,11 @@ export class DmnEditorInterface implements Editor {
   public af_componentTitle: "DMN Editor";
 
   constructor(
-    private readonly envelopeContext: 
KogitoEditorEnvelopeContextType<KogitoEditorEnvelopeApi, 
KogitoEditorChannelApi>,
-    private readonly initArgs: EditorInitArgs
+    protected readonly envelopeContext: KogitoEditorEnvelopeContextType<
+      KogitoEditorEnvelopeApi,
+      KogitoEditorChannelApi
+    >,
+    protected readonly initArgs: EditorInitArgs
   ) {}
 
   // Not in-editor
@@ -101,16 +104,18 @@ export class DmnEditorInterface implements Editor {
 }
 
 // This component is a wrapper. It memoizes the DmnEditorRoot props beforing 
rendering it.
-function DmnEditorRootWrapper({
+export function DmnEditorRootWrapper({
   envelopeContext,
   exposing,
   workspaceRootAbsolutePosixPath,
   isReadOnly,
+  onOpenedBoxedExpressionEditorNodeChange,
 }: {
   envelopeContext?: KogitoEditorEnvelopeContextType<KogitoEditorEnvelopeApi, 
KogitoEditorChannelApi>;
   exposing: (s: DmnEditorRoot) => void;
   workspaceRootAbsolutePosixPath: string;
   isReadOnly: boolean;
+  onOpenedBoxedExpressionEditorNodeChange?: (newOpenedNodeId: string | 
undefined) => void;
 }) {
   const onNewEdit = useCallback(
     (workspaceEdit: WorkspaceEdit) => {
@@ -153,6 +158,7 @@ function DmnEditorRootWrapper({
       onOpenFileFromNormalizedPosixPathRelativeToTheWorkspaceRoot={
         onOpenFileFromNormalizedPosixPathRelativeToTheWorkspaceRoot
       }
+      
onOpenedBoxedExpressionEditorNodeChange={onOpenedBoxedExpressionEditorNodeChange}
       workspaceRootAbsolutePosixPath={workspaceRootAbsolutePosixPath}
       keyboardShortcutsService={envelopeContext?.services.keyboardShortcuts}
       isReadOnly={isReadOnly}
diff --git a/packages/dmn-editor-envelope/src/DmnEditorRoot.tsx 
b/packages/dmn-editor-envelope/src/DmnEditorRoot.tsx
index f04d98684c7..e7f24569353 100644
--- a/packages/dmn-editor-envelope/src/DmnEditorRoot.tsx
+++ b/packages/dmn-editor-envelope/src/DmnEditorRoot.tsx
@@ -58,6 +58,7 @@ export type DmnEditorRootProps = {
   onRequestWorkspaceFilesList: 
WorkspaceChannelApi["kogitoWorkspace_resourceListRequest"];
   onRequestWorkspaceFileContent: 
WorkspaceChannelApi["kogitoWorkspace_resourceContentRequest"];
   onOpenFileFromNormalizedPosixPathRelativeToTheWorkspaceRoot: 
WorkspaceChannelApi["kogitoWorkspace_openFile"];
+  onOpenedBoxedExpressionEditorNodeChange?: (newOpenedNodeId: string | 
undefined) => void;
   workspaceRootAbsolutePosixPath: string;
   keyboardShortcutsService: KeyboardShortcutsService | undefined;
   isReadOnly: boolean;
@@ -492,6 +493,7 @@ export class DmnEditorRoot extends 
React.Component<DmnEditorRootProps, DmnEditor
               issueTrackerHref={""}
               isReadOnly={this.state.isReadOnly}
               onModelChange={this.onModelChange}
+              
onOpenedBoxedExpressionEditorNodeChange={this.props.onOpenedBoxedExpressionEditorNodeChange}
               
onRequestExternalModelsAvailableToInclude={this.onRequestExternalModelsAvailableToInclude}
               // (begin) All paths coming from inside the DmnEditor component 
are paths relative to the open file.
               
onRequestExternalModelByPath={this.onRequestExternalModelByPathsRelativeToTheOpenFile}
diff --git a/packages/dmn-editor-envelope/src/NewDmnEditorEnvelopeApi.ts 
b/packages/dmn-editor-envelope/src/NewDmnEditorEnvelopeApi.ts
index 51b567f9b56..c453ac2e3d8 100644
--- a/packages/dmn-editor-envelope/src/NewDmnEditorEnvelopeApi.ts
+++ b/packages/dmn-editor-envelope/src/NewDmnEditorEnvelopeApi.ts
@@ -19,12 +19,16 @@
 
 import { KogitoEditorEnvelopeApi } from "@kie-tools-core/editor/dist/api";
 import { NewDmnEditorTypes } from "./NewDmnEditorTypes";
+import { SharedValueProvider } from "../../envelope-bus/dist/api";
 
 export interface NewDmnEditorEnvelopeApi extends KogitoEditorEnvelopeApi {
   /**
    * Open boxed expression editor for given node
    * @param nodeId id of the node to open
    */
-  dmnEditor_openBoxedExpressionEditor(nodeId: string): void;
+  newDmnEditor_openBoxedExpressionEditor(nodeId: string): void;
+
+  newDmnEditor_openedBoxedExpressionEditorNodeId(): SharedValueProvider<string 
| undefined>;
+
   newDmnEditor_showDmnEvaluationResults(evaluationResultsByNodeId: 
NewDmnEditorTypes.EvaluationResultsByNodeId): void;
 }
diff --git a/packages/dmn-editor-envelope/src/NewDmnEditorEnvelopeApiFactory.ts 
b/packages/dmn-editor-envelope/src/NewDmnEditorEnvelopeApiFactory.ts
index 7c7ae5c2aa5..24fbc6e77df 100644
--- a/packages/dmn-editor-envelope/src/NewDmnEditorEnvelopeApiFactory.ts
+++ b/packages/dmn-editor-envelope/src/NewDmnEditorEnvelopeApiFactory.ts
@@ -25,6 +25,7 @@ import { NewDmnEditorEnvelopeApi } from 
"./NewDmnEditorEnvelopeApi";
 import { NewDmnEditorChannelApi } from "./NewDmnEditorChannelApi";
 import { NewDmnEditorFactory } from "./NewDmnEditorFactory";
 import { NewDmnEditorTypes } from "./NewDmnEditorTypes";
+import { SharedValueProvider } from "../../envelope-bus/dist/api";
 
 export type NewDmnEnvelopeApiFactoryArgs = EnvelopeApiFactoryArgs<
   NewDmnEditorEnvelopeApi,
@@ -41,10 +42,16 @@ export class NewDmnEditorEnvelopeApiImpl
     super(dmnArgs, new NewDmnEditorFactory());
   }
 
-  public dmnEditor_openBoxedExpressionEditor(nodeId: string): void {
+  public newDmnEditor_openBoxedExpressionEditor(nodeId: string): void {
     this.getEditorOrThrowError().openBoxedExpressionEditor(nodeId);
   }
 
+  public newDmnEditor_openedBoxedExpressionEditorNodeId(): 
SharedValueProvider<string | undefined> {
+    return {
+      defaultValue: undefined,
+    };
+  }
+
   public newDmnEditor_showDmnEvaluationResults(
     evaluationResultsByNodeId: NewDmnEditorTypes.EvaluationResultsByNodeId
   ): void {
diff --git a/packages/dmn-editor-envelope/src/NewDmnEditorFactory.tsx 
b/packages/dmn-editor-envelope/src/NewDmnEditorFactory.tsx
index 3fca40f33df..373cf831689 100644
--- a/packages/dmn-editor-envelope/src/NewDmnEditorFactory.tsx
+++ b/packages/dmn-editor-envelope/src/NewDmnEditorFactory.tsx
@@ -18,9 +18,14 @@
  */
 
 import * as React from "react";
-import { EditorFactory, EditorInitArgs, KogitoEditorEnvelopeContextType } from 
"@kie-tools-core/editor/dist/api";
+import {
+  DEFAULT_WORKSPACE_ROOT_ABSOLUTE_POSIX_PATH,
+  EditorFactory,
+  EditorInitArgs,
+  KogitoEditorEnvelopeContextType,
+} from "@kie-tools-core/editor/dist/api";
 import { NewDmnEditorChannelApi } from "./NewDmnEditorChannelApi";
-import { DmnEditorInterface } from "./DmnEditorFactory";
+import { DmnEditorInterface, DmnEditorRootWrapper } from "./DmnEditorFactory";
 import { NewDmnEditorEnvelopeApi } from "./NewDmnEditorEnvelopeApi";
 import { NewDmnEditorTypes } from "./NewDmnEditorTypes";
 
@@ -47,4 +52,22 @@ export class NewDmnEditorInterface extends 
DmnEditorInterface {
   public showDmnEvaluationResults(evaluationResultsByNodeId: 
NewDmnEditorTypes.EvaluationResultsByNodeId): void {
     this.self.showDmnEvaluationResults(evaluationResultsByNodeId);
   }
+
+  public af_componentRoot() {
+    return (
+      <DmnEditorRootWrapper
+        exposing={(dmnEditorRoot) => (this.self = dmnEditorRoot)}
+        envelopeContext={this.envelopeContext}
+        workspaceRootAbsolutePosixPath={
+          this.initArgs.workspaceRootAbsolutePosixPath ?? 
DEFAULT_WORKSPACE_ROOT_ABSOLUTE_POSIX_PATH
+        }
+        isReadOnly={this.initArgs.isReadOnly}
+        onOpenedBoxedExpressionEditorNodeChange={(newOpenedNodeId) => {
+          (
+            this.envelopeContext as 
KogitoEditorEnvelopeContextType<NewDmnEditorEnvelopeApi, NewDmnEditorChannelApi>
+          
)?.shared.newDmnEditor_openedBoxedExpressionEditorNodeId.set(newOpenedNodeId);
+        }}
+      />
+    );
+  }
 }
diff --git a/packages/dmn-editor/src/DmnEditor.tsx 
b/packages/dmn-editor/src/DmnEditor.tsx
index 81c185aa606..711b6bc69e5 100644
--- a/packages/dmn-editor/src/DmnEditor.tsx
+++ b/packages/dmn-editor/src/DmnEditor.tsx
@@ -186,16 +186,20 @@ export type DmnEditorProps = {
    * Notifies the caller when the DMN Editor performs a new edit after the 
debounce time.
    */
   onModelDebounceStateChanged?: (changed: boolean) => void;
+
+  onOpenedBoxedExpressionEditorNodeChange?: (newOpenedNodeId: string | 
undefined) => void;
 };
 
 export const DmnEditorInternal = ({
   model,
   originalVersion,
   onModelChange,
+  onOpenedBoxedExpressionEditorNodeChange,
   onModelDebounceStateChanged,
   forwardRef,
 }: DmnEditorProps & { forwardRef?: React.Ref<DmnEditorRef> }) => {
   const boxedExpressionEditorActiveDrgElementId = useDmnEditorStore((s) => 
s.boxedExpressionEditor.activeDrgElementId);
+  const dmnEditorActiveTab = useDmnEditorStore((s) => s.navigation.tab);
   const isBeePropertiesPanelOpen = useDmnEditorStore((s) => 
s.boxedExpressionEditor.propertiesPanel.isOpen);
   const isDiagramPropertiesPanelOpen = useDmnEditorStore((s) => 
s.diagram.propertiesPanel.isOpen);
   const navigationTab = useDmnEditorStore((s) => s.navigation.tab);
@@ -207,6 +211,13 @@ export const DmnEditorInternal = ({
   const { dmnModelBeforeEditingRef, dmnEditorRootElementRef } = useDmnEditor();
   const { externalModelsByNamespace } = useExternalModels();
 
+  // Code to keep FormDmnOutputs.tsx selected card highlight in proper state
+  useEffect(() => {
+    onOpenedBoxedExpressionEditorNodeChange?.(
+      dmnEditorActiveTab === DmnEditorTab.EDITOR ? 
boxedExpressionEditorActiveDrgElementId : undefined
+    );
+  }, [boxedExpressionEditorActiveDrgElementId, dmnEditorActiveTab, 
onOpenedBoxedExpressionEditorNodeChange]);
+
   // Refs
   const diagramRef = useRef<DiagramRef>(null);
   const diagramContainerRef = useRef<HTMLDivElement>(null);
diff --git a/packages/dmn-editor/stories/dmnEditorStoriesWrapper.tsx 
b/packages/dmn-editor/stories/dmnEditorStoriesWrapper.tsx
index 64f95d23927..f6528d3f640 100644
--- a/packages/dmn-editor/stories/dmnEditorStoriesWrapper.tsx
+++ b/packages/dmn-editor/stories/dmnEditorStoriesWrapper.tsx
@@ -51,6 +51,11 @@ export function DmnEditorWrapper(props?: 
Partial<StorybookDmnEditorProps>) {
     [props?.onModelChange]
   );
 
+  const onOpenedBoxedExpressionEditorNodeChangeNoOperation = useMemo(
+    () => (newOpenedNodeId: string | undefined) => {},
+    []
+  );
+
   useEffect(() => {
     if (args.isReadOnly !== undefined) {
       setIsReadOnly(args.isReadOnly);
@@ -103,6 +108,7 @@ export function DmnEditorWrapper(props?: 
Partial<StorybookDmnEditorProps>) {
           originalVersion={props?.originalVersion ?? args.originalVersion}
           isReadOnly={isReadOnly}
           onModelChange={onModelChange}
+          
onOpenedBoxedExpressionEditorNodeChange={onOpenedBoxedExpressionEditorNodeChangeNoOperation}
           onRequestExternalModelByPath={props?.onRequestExternalModelByPath ?? 
args.onRequestExternalModelByPath}
           onRequestExternalModelsAvailableToInclude={
             props?.onRequestExternalModelsAvailableToInclude ?? 
args.onRequestExternalModelsAvailableToInclude
diff --git a/packages/form-dmn/src/FormDmnOutputs.tsx 
b/packages/form-dmn/src/FormDmnOutputs.tsx
index f9be937feae..362b7ed146b 100644
--- a/packages/form-dmn/src/FormDmnOutputs.tsx
+++ b/packages/form-dmn/src/FormDmnOutputs.tsx
@@ -66,11 +66,18 @@ export interface FormDmnOutputsProps {
   notificationsPanel: boolean;
   openEvaluationTab?: () => void;
   openBoxedExpressionEditor?: (nodeId: string) => void;
+  openedBoxedExpressionEditorNodeId: string | undefined;
 }
 
-export function FormDmnOutputs({ openEvaluationTab, openBoxedExpressionEditor, 
...props }: FormDmnOutputsProps) {
+export function FormDmnOutputs({
+  openEvaluationTab,
+  openBoxedExpressionEditor,
+  openedBoxedExpressionEditorNodeId,
+  ...props
+}: FormDmnOutputsProps) {
   const [formResultStatus, setFormResultStatus] = 
useState<FormDmnOutputsStatus>(FormDmnOutputsStatus.EMPTY);
   const [formResultError, setFormResultError] = useState<boolean>(false);
+
   const i18n = useMemo(() => {
     formDmnI18n.setLocale(props.locale ?? navigator.language);
     return formDmnI18n.getCurrent();
@@ -86,7 +93,7 @@ export function FormDmnOutputs({ openEvaluationTab, 
openBoxedExpressionEditor, .
       const updatedResult = document.getElementById(`${index}-dmn-result`);
       
updatedResult?.classList.add("kogito--editor__dmn-form-result__leaf-updated");
     });
-  }, [props.differences]);
+  }, [openedBoxedExpressionEditorNodeId, props.differences]);
 
   const onAnimationEnd = useCallback((e: React.AnimationEvent<HTMLElement>, 
index) => {
     e.preventDefault();
@@ -268,13 +275,17 @@ export function FormDmnOutputs({ openEvaluationTab, 
openBoxedExpressionEditor, .
           <Card
             id={`${index}-dmn-result`}
             isFlat={true}
-            className={"kogito--editor__dmn-form-result__results-card"}
+            className={
+              openedBoxedExpressionEditorNodeId === dmnFormResult.decisionId
+                ? "kogito--editor__dmn-form-result__results-card-highlight"
+                : "kogito--editor__dmn-form-result__results-card"
+            }
             onAnimationEnd={(e) => onAnimationEnd(e, index)}
           >
             <CardTitle>
               <Flex justifyContent={{ default: "justifyContentSpaceBetween" }} 
flexWrap={{ default: "nowrap" }}>
                 <Title headingLevel={"h2"}>{dmnFormResult.decisionName}</Title>
-                {onOpenBoxedExpressionEditor !== undefined && (
+                {openBoxedExpressionEditor !== undefined && (
                   <Button
                     variant={"plain"}
                     title={`Open '${dmnFormResult.decisionName}' expression`}
@@ -289,7 +300,15 @@ export function FormDmnOutputs({ openEvaluationTab, 
openBoxedExpressionEditor, .
           </Card>
         </div>
       )),
-    [onAnimationEnd, onOpenBoxedExpressionEditor, props.results, result, 
resultStatus]
+    [
+      props.results,
+      openedBoxedExpressionEditorNodeId,
+      openBoxedExpressionEditor,
+      result,
+      resultStatus,
+      onAnimationEnd,
+      onOpenBoxedExpressionEditor,
+    ]
   );
 
   const formResultErrorMessage = useMemo(
diff --git a/packages/form-dmn/src/styles.scss 
b/packages/form-dmn/src/styles.scss
index 781137f09c0..db02a89dc7b 100644
--- a/packages/form-dmn/src/styles.scss
+++ b/packages/form-dmn/src/styles.scss
@@ -57,3 +57,7 @@
 .kogito--editor__dmn-form-result__results-card {
   border: 0 !important;
 }
+
+.pf-c-card.pf-m-flat.kogito--editor__dmn-form-result__results-card-highlight {
+  border: var(--pf-global--BorderWidth--md) solid 
var(--pf-global--BorderColor--200);
+}
diff --git a/packages/form-dmn/tests/FormDmnOutputs.test.tsx 
b/packages/form-dmn/tests/FormDmnOutputs.test.tsx
index 885a8bb5ad7..35af60ecfcd 100644
--- a/packages/form-dmn/tests/FormDmnOutputs.test.tsx
+++ b/packages/form-dmn/tests/FormDmnOutputs.test.tsx
@@ -28,6 +28,7 @@ const props: FormDmnOutputsProps = {
   locale: "en",
   notificationsPanel: true,
   openEvaluationTab: () => {},
+  openedBoxedExpressionEditorNodeId: undefined,
 };
 
 describe("FormDmnOutputs tests", () => {
diff --git 
a/packages/online-editor/src/dmnRunner/DmnRunnerDrawerPanelContent.tsx 
b/packages/online-editor/src/dmnRunner/DmnRunnerDrawerPanelContent.tsx
index ef085998303..f3d43d180f4 100644
--- a/packages/online-editor/src/dmnRunner/DmnRunnerDrawerPanelContent.tsx
+++ b/packages/online-editor/src/dmnRunner/DmnRunnerDrawerPanelContent.tsx
@@ -41,6 +41,7 @@ import { DmnRunnerExtendedServicesError } from 
"./DmnRunnerContextProvider";
 import { MessageBusClientApi } from "@kie-tools-core/envelope-bus/dist/api";
 import { NewDmnEditorEnvelopeApi } from 
"@kie-tools/dmn-editor-envelope/dist/NewDmnEditorEnvelopeApi";
 import { useSettings } from "../settings/SettingsContext";
+import { useSharedValue } from "@kie-tools-core/envelope-bus/dist/hooks";
 
 enum ButtonPosition {
   INPUT,
@@ -165,6 +166,11 @@ export function DmnRunnerDrawerPanelContent() {
     onOpenPanel(PanelId.DMN_RUNNER_TABLE);
   }, [onOpenPanel, setDmnRunnerMode]);
 
+  const [openedBoxedExpressionNodeId, _] = useSharedValue(
+    (envelopeServer?.envelopeApi as 
MessageBusClientApi<NewDmnEditorEnvelopeApi>)?.shared
+      ?.newDmnEditor_openedBoxedExpressionEditorNodeId ?? undefined
+  );
+
   return (
     <DrawerPanelContent
       id={"kogito-panel-content"}
@@ -335,10 +341,11 @@ export function DmnRunnerDrawerPanelContent() {
                           ? (nodeId: string) => {
                               const newDmnEditorEnvelopeApi =
                                 envelopeServer?.envelopeApi as 
MessageBusClientApi<NewDmnEditorEnvelopeApi>;
-                              
newDmnEditorEnvelopeApi.notifications.dmnEditor_openBoxedExpressionEditor.send(nodeId);
+                              
newDmnEditorEnvelopeApi.notifications.newDmnEditor_openBoxedExpressionEditor.send(nodeId);
                             }
                           : undefined
                       }
+                      
openedBoxedExpressionEditorNodeId={openedBoxedExpressionNodeId}
                     />
                   </PageSection>
                 </div>
diff --git a/packages/online-editor/src/dmnRunner/DmnRunnerTable.tsx 
b/packages/online-editor/src/dmnRunner/DmnRunnerTable.tsx
index 4973de9a329..9799f00e5b2 100644
--- a/packages/online-editor/src/dmnRunner/DmnRunnerTable.tsx
+++ b/packages/online-editor/src/dmnRunner/DmnRunnerTable.tsx
@@ -152,7 +152,7 @@ export function DmnRunnerTable() {
                                   const newDmnEditorEnvelopeApi =
                                     envelopeServer?.envelopeApi as 
MessageBusClientApi<NewDmnEditorEnvelopeApi>;
 
-                                  
newDmnEditorEnvelopeApi.notifications.dmnEditor_openBoxedExpressionEditor.send(
+                                  
newDmnEditorEnvelopeApi.notifications.newDmnEditor_openBoxedExpressionEditor.send(
                                     nodeId
                                   );
                                 }


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to