From a8836387439ff69f21ee3b270166e23adefaf317 Mon Sep 17 00:00:00 2001
From: "kamil.orwat" <kamil.orwat@vml.com>
Date: Thu, 17 Apr 2025 15:25:43 +0200
Subject: [PATCH 1/7] Work in progress

---
 core/pom.xml                                  |  6 +++
 .../es/aem/acm/core/code/ArgumentType.java    |  1 +
 .../vml/es/aem/acm/core/code/Arguments.java   | 10 ++++
 .../acm/core/code/arg/DateTimeArgument.java   | 53 +++++++++++++++++++
 .../vml/es/aem/acm/core/util/JsonUtils.java   |  4 +-
 pom.xml                                       |  5 ++
 .../src/components/CodeArgumentInput.tsx      | 44 ++++++++++++++-
 ui.frontend/src/utils/api.types.ts            | 10 +++-
 8 files changed, 129 insertions(+), 4 deletions(-)
 create mode 100644 core/src/main/java/com/vml/es/aem/acm/core/code/arg/DateTimeArgument.java

diff --git a/core/pom.xml b/core/pom.xml
index 139bffc8..98d832f8 100644
--- a/core/pom.xml
+++ b/core/pom.xml
@@ -248,6 +248,12 @@ DynamicImport-Package: *
             <groupId>com.fasterxml.jackson.core</groupId>
             <scope>provided</scope>
         </dependency>
+        <dependency>
+            <groupId>com.fasterxml.jackson.datatype</groupId>
+            <artifactId>jackson-datatype-jsr310</artifactId>
+            <version>2.17.2</version>
+            <scope>provided</scope>
+        </dependency>
         <dependency>
             <groupId>com.day.cq</groupId>
             <artifactId>cq-replication</artifactId>
diff --git a/core/src/main/java/com/vml/es/aem/acm/core/code/ArgumentType.java b/core/src/main/java/com/vml/es/aem/acm/core/code/ArgumentType.java
index eeb3badf..c160d469 100644
--- a/core/src/main/java/com/vml/es/aem/acm/core/code/ArgumentType.java
+++ b/core/src/main/java/com/vml/es/aem/acm/core/code/ArgumentType.java
@@ -2,6 +2,7 @@
 
 public enum ArgumentType {
     BOOL,
+    DATETIME,
     INTEGER,
     DECIMAL,
     STRING,
diff --git a/core/src/main/java/com/vml/es/aem/acm/core/code/Arguments.java b/core/src/main/java/com/vml/es/aem/acm/core/code/Arguments.java
index f2d23b20..32564dd5 100644
--- a/core/src/main/java/com/vml/es/aem/acm/core/code/Arguments.java
+++ b/core/src/main/java/com/vml/es/aem/acm/core/code/Arguments.java
@@ -86,6 +86,16 @@ public void bool(String name, Closure<BoolArgument> options) {
         add(argument);
     }
 
+    public void dateTime(String name) {
+        dateTime(name, null);
+    }
+
+    public void dateTime(String name, Closure<DateTimeArgument> options) {
+        DateTimeArgument argument = new DateTimeArgument(name);
+        GroovyUtils.with(argument, options);
+        add(argument);
+    }
+
     public void string(String name) {
         string(name, null);
     }
diff --git a/core/src/main/java/com/vml/es/aem/acm/core/code/arg/DateTimeArgument.java b/core/src/main/java/com/vml/es/aem/acm/core/code/arg/DateTimeArgument.java
new file mode 100644
index 00000000..998f2714
--- /dev/null
+++ b/core/src/main/java/com/vml/es/aem/acm/core/code/arg/DateTimeArgument.java
@@ -0,0 +1,53 @@
+package com.vml.es.aem.acm.core.code.arg;
+
+import com.vml.es.aem.acm.core.code.Argument;
+import com.vml.es.aem.acm.core.code.ArgumentType;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.util.Arrays;
+
+public class DateTimeArgument extends Argument<LocalDateTime> {
+    private DateTimeArgument.Display display = DateTimeArgument.Display.DATETIME;
+
+    public DateTimeArgument(String name) {
+        super(name, ArgumentType.DATETIME);
+    }
+
+    public DateTimeArgument.Display getDisplay() {
+        return display;
+    }
+
+    public void setDisplay(String display) {
+        this.display = DateTimeArgument.Display.of(display);
+    }
+
+    public void setDisplay(DateTimeArgument.Display render) {
+        this.display = render;
+    }
+
+    // Cast LocalDate to LocalDateTime in case of DATE display type
+    public void setValue(LocalDate value) {
+        setValue(value.atStartOfDay());
+    }
+
+    public void date() {
+        this.display = DateTimeArgument.Display.DATE;
+    }
+
+    public void dateTime() {
+        this.display = DateTimeArgument.Display.DATETIME;
+    }
+
+    public enum Display {
+        DATE,
+        DATETIME;
+
+        public static DateTimeArgument.Display of(String name) {
+            return Arrays.stream(DateTimeArgument.Display.values())
+                    .filter(r -> r.name().equalsIgnoreCase(name))
+                    .findFirst()
+                    .orElseThrow(() -> new IllegalArgumentException(
+                            String.format("Bool argument cannot be displayed as '%s'!", name)));
+        }
+    }
+}
diff --git a/core/src/main/java/com/vml/es/aem/acm/core/util/JsonUtils.java b/core/src/main/java/com/vml/es/aem/acm/core/util/JsonUtils.java
index 37d6d133..0160f209 100644
--- a/core/src/main/java/com/vml/es/aem/acm/core/util/JsonUtils.java
+++ b/core/src/main/java/com/vml/es/aem/acm/core/util/JsonUtils.java
@@ -3,6 +3,7 @@
 import com.fasterxml.jackson.databind.ObjectMapper;
 import com.fasterxml.jackson.databind.ObjectWriter;
 import com.fasterxml.jackson.databind.SerializationFeature;
+import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
 import java.io.ByteArrayInputStream;
 import java.io.IOException;
 import java.io.InputStream;
@@ -17,7 +18,8 @@ public final class JsonUtils {
 
     public static final ObjectMapper MAPPER = new ObjectMapper()
             .configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false)
-            .configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
+            .configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false)
+            .registerModule(new JavaTimeModule());
 
     public static final ObjectWriter COMPACT_WRITER = MAPPER.writer();
 
diff --git a/pom.xml b/pom.xml
index 97bfa5ff..21df35af 100644
--- a/pom.xml
+++ b/pom.xml
@@ -254,6 +254,11 @@ Bundle-DocURL:
                             <artifactId>scriptingbundle-maven-plugin</artifactId>
                             <version>0.5.0</version>
                         </dependency>
+                        <dependency>
+                            <groupId>com.fasterxml.jackson.datatype</groupId>
+                            <artifactId>jackson-datatype-jsr310</artifactId>
+                            <version>2.17.2</version>
+                        </dependency>
                     </dependencies>
                 </plugin>
                 <plugin>
diff --git a/ui.frontend/src/components/CodeArgumentInput.tsx b/ui.frontend/src/components/CodeArgumentInput.tsx
index 226d8a4f..db847e1a 100644
--- a/ui.frontend/src/components/CodeArgumentInput.tsx
+++ b/ui.frontend/src/components/CodeArgumentInput.tsx
@@ -1,12 +1,38 @@
-import { Checkbox, CheckboxGroup, Flex, Item, ListView, NumberField, Picker, Radio, RadioGroup, Switch, TextArea, TextField, View } from '@adobe/react-spectrum';
+import {
+  Checkbox,
+  CheckboxGroup,
+  DatePicker,
+  Flex,
+  Item,
+  ListView,
+  NumberField,
+  Picker,
+  Radio,
+  RadioGroup,
+  Switch,
+  TextArea,
+  TextField,
+  View
+} from '@adobe/react-spectrum';
 import { Editor } from '@monaco-editor/react';
 import { Field } from '@react-spectrum/label';
 import React from 'react';
 import { Controller, useFormContext } from 'react-hook-form';
 import useFormCrossFieldValidation from '../hooks/form.ts';
-import { Argument, ArgumentValue, isBoolArgument, isMultiSelectArgument, isNumberArgument, isSelectArgument, isStringArgument, isTextArgument } from '../utils/api.types.ts';
+import {
+  Argument,
+  ArgumentValue,
+  isBoolArgument,
+  isDateTimeArgument,
+  isMultiSelectArgument,
+  isNumberArgument,
+  isSelectArgument,
+  isStringArgument,
+  isTextArgument
+} from '../utils/api.types.ts';
 import { Strings } from '../utils/strings.ts';
 import styles from "./CodeArgumentInput.module.css"
+import {Dates} from "../utils/dates.ts";
 
 interface CodeArgumentInputProps {
   arg: Argument<ArgumentValue>;
@@ -63,6 +89,20 @@ const CodeArgumentInput: React.FC<CodeArgumentInputProps> = ({ arg }) => {
           )}
         />
       );
+    } else if (isDateTimeArgument(arg)) {
+      console.log(arg)
+      return (
+        <Controller
+          name={arg.name}
+          control={control}
+          rules={controllerRules(arg)}
+          render={({ field, fieldState }) => (
+            <View key={arg.name} marginBottom="size-200">
+              <DatePicker {...field} value={Dates.toCalendarOrNull(field.value)} onChange={(dateValue) => field.onChange(dateValue?.toString())} granularity={arg.display === "DATETIME" ? "second" : "day"} label={argLabel(arg)} errorMessage={fieldState.error ? fieldState.error.message : undefined} validationState={fieldState.error ? 'invalid' : 'valid'} aria-label={`Argument '${arg.name}'`} width="100%" />
+            </View>
+          )}
+        />
+      )
     } else if (isStringArgument(arg)) {
       return (
         <Controller
diff --git a/ui.frontend/src/utils/api.types.ts b/ui.frontend/src/utils/api.types.ts
index cac5f825..435f6c58 100644
--- a/ui.frontend/src/utils/api.types.ts
+++ b/ui.frontend/src/utils/api.types.ts
@@ -23,7 +23,7 @@ export type Description = {
   };
 };
 
-export type ArgumentType = 'BOOL' | 'STRING' | 'TEXT' | 'SELECT' | 'MULTISELECT' | 'INTEGER' | 'DECIMAL';
+export type ArgumentType = 'BOOL' | 'STRING' | 'TEXT' | 'SELECT' | 'MULTISELECT' | 'INTEGER' | 'DECIMAL' | 'DATETIME';
 export type ArgumentValue = string | string[] | number | number[] | boolean | null | undefined;
 export type ArgumentValues = Record<string, ArgumentValue>;
 
@@ -43,6 +43,10 @@ export type BoolArgument = Argument<boolean> & {
   display: 'SWITCHER' | 'CHECKBOX';
 };
 
+export type DateTimeArgument = Argument<string> & {
+  display: 'DATE' | 'DATETIME';
+};
+
 export type TextArgument = Argument<string> & {
   language?: string;
 };
@@ -70,6 +74,10 @@ export function isBoolArgument(arg: Argument<ArgumentValue>): arg is BoolArgumen
   return arg.type === 'BOOL';
 }
 
+export function isDateTimeArgument(arg: Argument<ArgumentValue>): arg is DateTimeArgument {
+  return arg.type === 'DATETIME';
+}
+
 export function isTextArgument(arg: Argument<ArgumentValue>): arg is TextArgument {
   return arg.type === 'TEXT';
 }

From db7fa5853d6e23f7ea6da7ec760ab427119ed142 Mon Sep 17 00:00:00 2001
From: "kamil.orwat" <kamil.orwat@vml.com>
Date: Fri, 18 Apr 2025 09:26:38 +0200
Subject: [PATCH 2/7] Implementend min and max values

---
 .../acm/core/code/arg/DateTimeArgument.java   | 54 ++++++++++----
 .../src/components/CodeArgumentInput.tsx      | 70 +++++++++++++------
 ui.frontend/src/utils/api.types.ts            |  4 +-
 3 files changed, 91 insertions(+), 37 deletions(-)

diff --git a/core/src/main/java/com/vml/es/aem/acm/core/code/arg/DateTimeArgument.java b/core/src/main/java/com/vml/es/aem/acm/core/code/arg/DateTimeArgument.java
index 998f2714..2b0dbdde 100644
--- a/core/src/main/java/com/vml/es/aem/acm/core/code/arg/DateTimeArgument.java
+++ b/core/src/main/java/com/vml/es/aem/acm/core/code/arg/DateTimeArgument.java
@@ -7,43 +7,71 @@
 import java.util.Arrays;
 
 public class DateTimeArgument extends Argument<LocalDateTime> {
-    private DateTimeArgument.Display display = DateTimeArgument.Display.DATETIME;
+    private DateTimeArgument.Variant variant = DateTimeArgument.Variant.DATETIME;
+
+    private LocalDateTime min;
+
+    private LocalDateTime max;
 
     public DateTimeArgument(String name) {
         super(name, ArgumentType.DATETIME);
     }
 
-    public DateTimeArgument.Display getDisplay() {
-        return display;
+    public LocalDateTime getMin() {
+        return min;
+    }
+
+    public void setMin(LocalDateTime min) {
+        this.min = min;
+    }
+
+    public void setMin(LocalDate min) {
+        this.min = min.atStartOfDay();
+    }
+
+    public LocalDateTime getMax() {
+        return max;
+    }
+
+    public void setMax(LocalDateTime max) {
+        this.max = max;
+    }
+
+    public void setMax(LocalDate max) {
+        this.max = max.atStartOfDay();
+    }
+
+    public DateTimeArgument.Variant getVariant() {
+        return variant;
     }
 
-    public void setDisplay(String display) {
-        this.display = DateTimeArgument.Display.of(display);
+    public void setVariant(String variant) {
+        this.variant = DateTimeArgument.Variant.of(variant);
     }
 
-    public void setDisplay(DateTimeArgument.Display render) {
-        this.display = render;
+    public void setVariant(DateTimeArgument.Variant render) {
+        this.variant = render;
     }
 
-    // Cast LocalDate to LocalDateTime in case of DATE display type
+    // Cast LocalDate to LocalDateTime in case of DATE variant
     public void setValue(LocalDate value) {
         setValue(value.atStartOfDay());
     }
 
     public void date() {
-        this.display = DateTimeArgument.Display.DATE;
+        this.variant = DateTimeArgument.Variant.DATE;
     }
 
     public void dateTime() {
-        this.display = DateTimeArgument.Display.DATETIME;
+        this.variant = DateTimeArgument.Variant.DATETIME;
     }
 
-    public enum Display {
+    public enum Variant {
         DATE,
         DATETIME;
 
-        public static DateTimeArgument.Display of(String name) {
-            return Arrays.stream(DateTimeArgument.Display.values())
+        public static DateTimeArgument.Variant of(String name) {
+            return Arrays.stream(DateTimeArgument.Variant.values())
                     .filter(r -> r.name().equalsIgnoreCase(name))
                     .findFirst()
                     .orElseThrow(() -> new IllegalArgumentException(
diff --git a/ui.frontend/src/components/CodeArgumentInput.tsx b/ui.frontend/src/components/CodeArgumentInput.tsx
index db847e1a..88b74728 100644
--- a/ui.frontend/src/components/CodeArgumentInput.tsx
+++ b/ui.frontend/src/components/CodeArgumentInput.tsx
@@ -14,10 +14,10 @@ import {
   TextField,
   View
 } from '@adobe/react-spectrum';
-import { Editor } from '@monaco-editor/react';
-import { Field } from '@react-spectrum/label';
+import {Editor} from '@monaco-editor/react';
+import {Field} from '@react-spectrum/label';
 import React from 'react';
-import { Controller, useFormContext } from 'react-hook-form';
+import {Controller, useFormContext} from 'react-hook-form';
 import useFormCrossFieldValidation from '../hooks/form.ts';
 import {
   Argument,
@@ -30,7 +30,7 @@ import {
   isStringArgument,
   isTextArgument
 } from '../utils/api.types.ts';
-import { Strings } from '../utils/strings.ts';
+import {Strings} from '../utils/strings.ts';
 import styles from "./CodeArgumentInput.module.css"
 import {Dates} from "../utils/dates.ts";
 
@@ -40,8 +40,8 @@ interface CodeArgumentInputProps {
   onChange: (name: string, value: ArgumentValue) => void;
 }
 
-const CodeArgumentInput: React.FC<CodeArgumentInputProps> = ({ arg }) => {
-  const { control, getValues } = useFormContext();
+const CodeArgumentInput: React.FC<CodeArgumentInputProps> = ({arg}) => {
+  const {control, getValues} = useFormContext();
   useFormCrossFieldValidation(arg.name);
 
   const controllerRules = (arg: Argument<ArgumentValue>) => ({
@@ -71,15 +71,17 @@ const CodeArgumentInput: React.FC<CodeArgumentInputProps> = ({ arg }) => {
           name={arg.name}
           control={control}
           rules={controllerRules(arg)}
-          render={({ field, fieldState }) => (
+          render={({field, fieldState}) => (
             <View key={arg.name} marginBottom="size-200">
               <Flex alignItems={'start'} justifyContent={'start'} direction={'column'}>
                 {arg.display === 'SWITCHER' ? (
-                  <Switch {...field} isSelected={field.value} onChange={field.onChange} aria-label={`Argument '${arg.name}'`}>
+                  <Switch {...field} isSelected={field.value} onChange={field.onChange}
+                          aria-label={`Argument '${arg.name}'`}>
                     {argLabel(arg)}
                   </Switch>
                 ) : (
-                  <Checkbox {...field} isSelected={field.value} isInvalid={!!fieldState.error} onChange={field.onChange} aria-label={`Argument '${arg.name}'`}>
+                  <Checkbox {...field} isSelected={field.value} isInvalid={!!fieldState.error} onChange={field.onChange}
+                            aria-label={`Argument '${arg.name}'`}>
                     {argLabel(arg)}
                   </Checkbox>
                 )}
@@ -96,9 +98,18 @@ const CodeArgumentInput: React.FC<CodeArgumentInputProps> = ({ arg }) => {
           name={arg.name}
           control={control}
           rules={controllerRules(arg)}
-          render={({ field, fieldState }) => (
+          render={({field, fieldState}) => (
             <View key={arg.name} marginBottom="size-200">
-              <DatePicker {...field} value={Dates.toCalendarOrNull(field.value)} onChange={(dateValue) => field.onChange(dateValue?.toString())} granularity={arg.display === "DATETIME" ? "second" : "day"} label={argLabel(arg)} errorMessage={fieldState.error ? fieldState.error.message : undefined} validationState={fieldState.error ? 'invalid' : 'valid'} aria-label={`Argument '${arg.name}'`} width="100%" />
+              <DatePicker {...field}
+                          minValue={arg.min !== null ? Dates.toCalendarOrNull(arg.min) : undefined}
+                          maxValue={arg.max !== null ? Dates.toCalendarOrNull(arg.max) : undefined}
+                          value={Dates.toCalendarOrNull(field.value)}
+                          onChange={(dateValue) => field.onChange(dateValue?.toString())}
+                          granularity={arg.variant === "DATETIME" ? "second" : "day"}
+                          label={argLabel(arg)}
+                          errorMessage={fieldState.error ? fieldState.error.message : undefined}
+                          validationState={fieldState.error ? 'invalid' : 'valid'}
+                          aria-label={`Argument '${arg.name}'`} width="100%"/>
             </View>
           )}
         />
@@ -109,9 +120,12 @@ const CodeArgumentInput: React.FC<CodeArgumentInputProps> = ({ arg }) => {
           name={arg.name}
           control={control}
           rules={controllerRules(arg)}
-          render={({ field, fieldState }) => (
+          render={({field, fieldState}) => (
             <View key={arg.name} marginBottom="size-200">
-              <TextField {...field} label={argLabel(arg)} errorMessage={fieldState.error ? fieldState.error.message : undefined} validationState={fieldState.error ? 'invalid' : 'valid'} aria-label={`Argument '${arg.name}'`} width="100%" />
+              <TextField {...field} label={argLabel(arg)}
+                         errorMessage={fieldState.error ? fieldState.error.message : undefined}
+                         validationState={fieldState.error ? 'invalid' : 'valid'} aria-label={`Argument '${arg.name}'`}
+                         width="100%"/>
             </View>
           )}
         />
@@ -122,18 +136,26 @@ const CodeArgumentInput: React.FC<CodeArgumentInputProps> = ({ arg }) => {
           name={arg.name}
           control={control}
           rules={controllerRules(arg)}
-          render={({ field, fieldState }) => (
+          render={({field, fieldState}) => (
             <View key={arg.name} marginY="size-100">
               {arg.language ? (
-                <Field label={argLabel(arg)} description={`Language: ${arg.language}`} width="100%" errorMessage={fieldState.error ? fieldState.error.message : undefined} validationState={fieldState.error ? 'invalid' : 'valid'}>
+                <Field label={argLabel(arg)} description={`Language: ${arg.language}`} width="100%"
+                       errorMessage={fieldState.error ? fieldState.error.message : undefined}
+                       validationState={fieldState.error ? 'invalid' : 'valid'}>
                   <div>
-                    <View width="100%" backgroundColor="gray-800" borderWidth="thin" position="relative" borderColor="dark" height="100%" borderRadius="medium" padding="size-50">
-                      <Editor aria-label={`Argument '${arg.name}'`} language={arg.language} theme="vs-dark" height="200px" options={{ scrollBeyondLastLine: false }} value={field.value?.toString() || ''} onChange={field.onChange} />
+                    <View width="100%" backgroundColor="gray-800" borderWidth="thin" position="relative"
+                          borderColor="dark" height="100%" borderRadius="medium" padding="size-50">
+                      <Editor aria-label={`Argument '${arg.name}'`} language={arg.language} theme="vs-dark"
+                              height="200px" options={{scrollBeyondLastLine: false}}
+                              value={field.value?.toString() || ''} onChange={field.onChange}/>
                     </View>
                   </div>
                 </Field>
               ) : (
-                <TextArea {...field} label={argLabel(arg)} errorMessage={fieldState.error ? fieldState.error.message : undefined} validationState={fieldState.error ? 'invalid' : 'valid'} aria-label={`Argument '${arg.name}'`} width="100%" />
+                <TextArea {...field} label={argLabel(arg)}
+                          errorMessage={fieldState.error ? fieldState.error.message : undefined}
+                          validationState={fieldState.error ? 'invalid' : 'valid'} aria-label={`Argument '${arg.name}'`}
+                          width="100%"/>
               )}
             </View>
           )}
@@ -146,7 +168,7 @@ const CodeArgumentInput: React.FC<CodeArgumentInputProps> = ({ arg }) => {
           name={arg.name}
           control={control}
           rules={controllerRules(arg)}
-          render={({ field, fieldState }) => (
+          render={({field, fieldState}) => (
             <View key={arg.name} marginBottom="size-200">
               {display === 'RADIO' ? (
                 <RadioGroup
@@ -189,7 +211,7 @@ const CodeArgumentInput: React.FC<CodeArgumentInputProps> = ({ arg }) => {
           name={arg.name}
           control={control}
           rules={controllerRules(arg)}
-          render={({ field, fieldState }) => (
+          render={({field, fieldState}) => (
             <View key={arg.name} marginBottom="size-200">
               {display === 'CHECKBOX' ? (
                 <CheckboxGroup
@@ -207,7 +229,9 @@ const CodeArgumentInput: React.FC<CodeArgumentInputProps> = ({ arg }) => {
                   ))}
                 </CheckboxGroup>
               ) : (
-                <Field label={argLabel(arg)} width="100%" errorMessage={fieldState.error ? fieldState.error.message : undefined} validationState={fieldState.error ? 'invalid' : 'valid'}>
+                <Field label={argLabel(arg)} width="100%"
+                       errorMessage={fieldState.error ? fieldState.error.message : undefined}
+                       validationState={fieldState.error ? 'invalid' : 'valid'}>
                   <div>
                     <ListView
                       {...field}
@@ -234,7 +258,7 @@ const CodeArgumentInput: React.FC<CodeArgumentInputProps> = ({ arg }) => {
           name={arg.name}
           control={control}
           rules={controllerRules(arg)}
-          render={({ field, fieldState }) => (
+          render={({field, fieldState}) => (
             <View key={arg.name} marginBottom="size-200">
               <NumberField
                 {...field}
@@ -244,7 +268,7 @@ const CodeArgumentInput: React.FC<CodeArgumentInputProps> = ({ arg }) => {
                 minValue={arg.min !== null ? arg.min : undefined}
                 maxValue={arg.max !== null ? arg.max : undefined}
                 hideStepper={arg.type === 'DECIMAL'}
-                formatOptions={arg.type === 'INTEGER' ? { maximumFractionDigits: 0 } : undefined}
+                formatOptions={arg.type === 'INTEGER' ? {maximumFractionDigits: 0} : undefined}
                 aria-label={`Argument '${arg.name}'`}
               />
             </View>
diff --git a/ui.frontend/src/utils/api.types.ts b/ui.frontend/src/utils/api.types.ts
index 435f6c58..677d670a 100644
--- a/ui.frontend/src/utils/api.types.ts
+++ b/ui.frontend/src/utils/api.types.ts
@@ -44,7 +44,9 @@ export type BoolArgument = Argument<boolean> & {
 };
 
 export type DateTimeArgument = Argument<string> & {
-  display: 'DATE' | 'DATETIME';
+  variant: 'DATE' | 'DATETIME';
+  min: string;
+  max: string;
 };
 
 export type TextArgument = Argument<string> & {

From 5143804c2cd1d67f66dc70ab769befd4e71e3c46 Mon Sep 17 00:00:00 2001
From: kamil-orwat-vmltech <kamil.orwat@vml.com>
Date: Fri, 18 Apr 2025 09:38:30 +0200
Subject: [PATCH 3/7] Update ui.frontend/src/components/CodeArgumentInput.tsx

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
---
 ui.frontend/src/components/CodeArgumentInput.tsx | 1 -
 1 file changed, 1 deletion(-)

diff --git a/ui.frontend/src/components/CodeArgumentInput.tsx b/ui.frontend/src/components/CodeArgumentInput.tsx
index 88b74728..f0576f95 100644
--- a/ui.frontend/src/components/CodeArgumentInput.tsx
+++ b/ui.frontend/src/components/CodeArgumentInput.tsx
@@ -92,7 +92,6 @@ const CodeArgumentInput: React.FC<CodeArgumentInputProps> = ({arg}) => {
         />
       );
     } else if (isDateTimeArgument(arg)) {
-      console.log(arg)
       return (
         <Controller
           name={arg.name}

From e7ba18de0ed0bbc2465549230c6ee7d25091bc5e Mon Sep 17 00:00:00 2001
From: "kamil.orwat" <kamil.orwat@vml.com>
Date: Fri, 18 Apr 2025 09:39:15 +0200
Subject: [PATCH 4/7] Implementend min and max values

---
 .../java/com/vml/es/aem/acm/core/code/arg/DateTimeArgument.java | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/core/src/main/java/com/vml/es/aem/acm/core/code/arg/DateTimeArgument.java b/core/src/main/java/com/vml/es/aem/acm/core/code/arg/DateTimeArgument.java
index 2b0dbdde..2fca0715 100644
--- a/core/src/main/java/com/vml/es/aem/acm/core/code/arg/DateTimeArgument.java
+++ b/core/src/main/java/com/vml/es/aem/acm/core/code/arg/DateTimeArgument.java
@@ -75,7 +75,7 @@ public static DateTimeArgument.Variant of(String name) {
                     .filter(r -> r.name().equalsIgnoreCase(name))
                     .findFirst()
                     .orElseThrow(() -> new IllegalArgumentException(
-                            String.format("Bool argument cannot be displayed as '%s'!", name)));
+                            String.format("Datetime argument cannot be displayed as '%s'!", name)));
         }
     }
 }

From a01c23b564615d8529e84c0aa2be26b0289a2da8 Mon Sep 17 00:00:00 2001
From: "kamil.orwat" <kamil.orwat@vml.com>
Date: Fri, 18 Apr 2025 09:40:44 +0200
Subject: [PATCH 5/7] formatted the code

---
 .../components/CodeArgumentInput.module.css   |  14 +--
 .../src/components/CodeArgumentInput.tsx      | 117 ++++++------------
 2 files changed, 47 insertions(+), 84 deletions(-)

diff --git a/ui.frontend/src/components/CodeArgumentInput.module.css b/ui.frontend/src/components/CodeArgumentInput.module.css
index 15dfe7e1..4e1556a1 100644
--- a/ui.frontend/src/components/CodeArgumentInput.module.css
+++ b/ui.frontend/src/components/CodeArgumentInput.module.css
@@ -1,9 +1,9 @@
 /*Custom error state since react spectrum doesn't provide one for switch and checkbox component*/
 .error {
-    color: var(--spectrum-red-900);
-    font-size: var(--spectrum-global-dimension-font-size-75);
-    line-height: var(--spectrum-global-font-line-height-small);
-    letter-spacing: var(--spectrum-global-font-letter-spacing-none);
-    margin-inline-end: var(--spectrum-global-dimension-size-100);
-    font-family: var(--spectrum-global-font-family-base), sans-serif;
-}
\ No newline at end of file
+  color: var(--spectrum-red-900);
+  font-size: var(--spectrum-global-dimension-font-size-75);
+  line-height: var(--spectrum-global-font-line-height-small);
+  letter-spacing: var(--spectrum-global-font-letter-spacing-none);
+  margin-inline-end: var(--spectrum-global-dimension-size-100);
+  font-family: var(--spectrum-global-font-family-base), sans-serif;
+}
diff --git a/ui.frontend/src/components/CodeArgumentInput.tsx b/ui.frontend/src/components/CodeArgumentInput.tsx
index f0576f95..7324f858 100644
--- a/ui.frontend/src/components/CodeArgumentInput.tsx
+++ b/ui.frontend/src/components/CodeArgumentInput.tsx
@@ -1,38 +1,13 @@
-import {
-  Checkbox,
-  CheckboxGroup,
-  DatePicker,
-  Flex,
-  Item,
-  ListView,
-  NumberField,
-  Picker,
-  Radio,
-  RadioGroup,
-  Switch,
-  TextArea,
-  TextField,
-  View
-} from '@adobe/react-spectrum';
-import {Editor} from '@monaco-editor/react';
-import {Field} from '@react-spectrum/label';
+import { Checkbox, CheckboxGroup, DatePicker, Flex, Item, ListView, NumberField, Picker, Radio, RadioGroup, Switch, TextArea, TextField, View } from '@adobe/react-spectrum';
+import { Editor } from '@monaco-editor/react';
+import { Field } from '@react-spectrum/label';
 import React from 'react';
-import {Controller, useFormContext} from 'react-hook-form';
+import { Controller, useFormContext } from 'react-hook-form';
 import useFormCrossFieldValidation from '../hooks/form.ts';
-import {
-  Argument,
-  ArgumentValue,
-  isBoolArgument,
-  isDateTimeArgument,
-  isMultiSelectArgument,
-  isNumberArgument,
-  isSelectArgument,
-  isStringArgument,
-  isTextArgument
-} from '../utils/api.types.ts';
-import {Strings} from '../utils/strings.ts';
-import styles from "./CodeArgumentInput.module.css"
-import {Dates} from "../utils/dates.ts";
+import { Argument, ArgumentValue, isBoolArgument, isDateTimeArgument, isMultiSelectArgument, isNumberArgument, isSelectArgument, isStringArgument, isTextArgument } from '../utils/api.types.ts';
+import { Dates } from '../utils/dates.ts';
+import { Strings } from '../utils/strings.ts';
+import styles from './CodeArgumentInput.module.css';
 
 interface CodeArgumentInputProps {
   arg: Argument<ArgumentValue>;
@@ -40,8 +15,8 @@ interface CodeArgumentInputProps {
   onChange: (name: string, value: ArgumentValue) => void;
 }
 
-const CodeArgumentInput: React.FC<CodeArgumentInputProps> = ({arg}) => {
-  const {control, getValues} = useFormContext();
+const CodeArgumentInput: React.FC<CodeArgumentInputProps> = ({ arg }) => {
+  const { control, getValues } = useFormContext();
   useFormCrossFieldValidation(arg.name);
 
   const controllerRules = (arg: Argument<ArgumentValue>) => ({
@@ -71,17 +46,15 @@ const CodeArgumentInput: React.FC<CodeArgumentInputProps> = ({arg}) => {
           name={arg.name}
           control={control}
           rules={controllerRules(arg)}
-          render={({field, fieldState}) => (
+          render={({ field, fieldState }) => (
             <View key={arg.name} marginBottom="size-200">
               <Flex alignItems={'start'} justifyContent={'start'} direction={'column'}>
                 {arg.display === 'SWITCHER' ? (
-                  <Switch {...field} isSelected={field.value} onChange={field.onChange}
-                          aria-label={`Argument '${arg.name}'`}>
+                  <Switch {...field} isSelected={field.value} onChange={field.onChange} aria-label={`Argument '${arg.name}'`}>
                     {argLabel(arg)}
                   </Switch>
                 ) : (
-                  <Checkbox {...field} isSelected={field.value} isInvalid={!!fieldState.error} onChange={field.onChange}
-                            aria-label={`Argument '${arg.name}'`}>
+                  <Checkbox {...field} isSelected={field.value} isInvalid={!!fieldState.error} onChange={field.onChange} aria-label={`Argument '${arg.name}'`}>
                     {argLabel(arg)}
                   </Checkbox>
                 )}
@@ -97,34 +70,34 @@ const CodeArgumentInput: React.FC<CodeArgumentInputProps> = ({arg}) => {
           name={arg.name}
           control={control}
           rules={controllerRules(arg)}
-          render={({field, fieldState}) => (
+          render={({ field, fieldState }) => (
             <View key={arg.name} marginBottom="size-200">
-              <DatePicker {...field}
-                          minValue={arg.min !== null ? Dates.toCalendarOrNull(arg.min) : undefined}
-                          maxValue={arg.max !== null ? Dates.toCalendarOrNull(arg.max) : undefined}
-                          value={Dates.toCalendarOrNull(field.value)}
-                          onChange={(dateValue) => field.onChange(dateValue?.toString())}
-                          granularity={arg.variant === "DATETIME" ? "second" : "day"}
-                          label={argLabel(arg)}
-                          errorMessage={fieldState.error ? fieldState.error.message : undefined}
-                          validationState={fieldState.error ? 'invalid' : 'valid'}
-                          aria-label={`Argument '${arg.name}'`} width="100%"/>
+              <DatePicker
+                {...field}
+                minValue={arg.min !== null ? Dates.toCalendarOrNull(arg.min) : undefined}
+                maxValue={arg.max !== null ? Dates.toCalendarOrNull(arg.max) : undefined}
+                value={Dates.toCalendarOrNull(field.value)}
+                onChange={(dateValue) => field.onChange(dateValue?.toString())}
+                granularity={arg.variant === 'DATETIME' ? 'second' : 'day'}
+                label={argLabel(arg)}
+                errorMessage={fieldState.error ? fieldState.error.message : undefined}
+                validationState={fieldState.error ? 'invalid' : 'valid'}
+                aria-label={`Argument '${arg.name}'`}
+                width="100%"
+              />
             </View>
           )}
         />
-      )
+      );
     } else if (isStringArgument(arg)) {
       return (
         <Controller
           name={arg.name}
           control={control}
           rules={controllerRules(arg)}
-          render={({field, fieldState}) => (
+          render={({ field, fieldState }) => (
             <View key={arg.name} marginBottom="size-200">
-              <TextField {...field} label={argLabel(arg)}
-                         errorMessage={fieldState.error ? fieldState.error.message : undefined}
-                         validationState={fieldState.error ? 'invalid' : 'valid'} aria-label={`Argument '${arg.name}'`}
-                         width="100%"/>
+              <TextField {...field} label={argLabel(arg)} errorMessage={fieldState.error ? fieldState.error.message : undefined} validationState={fieldState.error ? 'invalid' : 'valid'} aria-label={`Argument '${arg.name}'`} width="100%" />
             </View>
           )}
         />
@@ -135,26 +108,18 @@ const CodeArgumentInput: React.FC<CodeArgumentInputProps> = ({arg}) => {
           name={arg.name}
           control={control}
           rules={controllerRules(arg)}
-          render={({field, fieldState}) => (
+          render={({ field, fieldState }) => (
             <View key={arg.name} marginY="size-100">
               {arg.language ? (
-                <Field label={argLabel(arg)} description={`Language: ${arg.language}`} width="100%"
-                       errorMessage={fieldState.error ? fieldState.error.message : undefined}
-                       validationState={fieldState.error ? 'invalid' : 'valid'}>
+                <Field label={argLabel(arg)} description={`Language: ${arg.language}`} width="100%" errorMessage={fieldState.error ? fieldState.error.message : undefined} validationState={fieldState.error ? 'invalid' : 'valid'}>
                   <div>
-                    <View width="100%" backgroundColor="gray-800" borderWidth="thin" position="relative"
-                          borderColor="dark" height="100%" borderRadius="medium" padding="size-50">
-                      <Editor aria-label={`Argument '${arg.name}'`} language={arg.language} theme="vs-dark"
-                              height="200px" options={{scrollBeyondLastLine: false}}
-                              value={field.value?.toString() || ''} onChange={field.onChange}/>
+                    <View width="100%" backgroundColor="gray-800" borderWidth="thin" position="relative" borderColor="dark" height="100%" borderRadius="medium" padding="size-50">
+                      <Editor aria-label={`Argument '${arg.name}'`} language={arg.language} theme="vs-dark" height="200px" options={{ scrollBeyondLastLine: false }} value={field.value?.toString() || ''} onChange={field.onChange} />
                     </View>
                   </div>
                 </Field>
               ) : (
-                <TextArea {...field} label={argLabel(arg)}
-                          errorMessage={fieldState.error ? fieldState.error.message : undefined}
-                          validationState={fieldState.error ? 'invalid' : 'valid'} aria-label={`Argument '${arg.name}'`}
-                          width="100%"/>
+                <TextArea {...field} label={argLabel(arg)} errorMessage={fieldState.error ? fieldState.error.message : undefined} validationState={fieldState.error ? 'invalid' : 'valid'} aria-label={`Argument '${arg.name}'`} width="100%" />
               )}
             </View>
           )}
@@ -167,7 +132,7 @@ const CodeArgumentInput: React.FC<CodeArgumentInputProps> = ({arg}) => {
           name={arg.name}
           control={control}
           rules={controllerRules(arg)}
-          render={({field, fieldState}) => (
+          render={({ field, fieldState }) => (
             <View key={arg.name} marginBottom="size-200">
               {display === 'RADIO' ? (
                 <RadioGroup
@@ -210,7 +175,7 @@ const CodeArgumentInput: React.FC<CodeArgumentInputProps> = ({arg}) => {
           name={arg.name}
           control={control}
           rules={controllerRules(arg)}
-          render={({field, fieldState}) => (
+          render={({ field, fieldState }) => (
             <View key={arg.name} marginBottom="size-200">
               {display === 'CHECKBOX' ? (
                 <CheckboxGroup
@@ -228,9 +193,7 @@ const CodeArgumentInput: React.FC<CodeArgumentInputProps> = ({arg}) => {
                   ))}
                 </CheckboxGroup>
               ) : (
-                <Field label={argLabel(arg)} width="100%"
-                       errorMessage={fieldState.error ? fieldState.error.message : undefined}
-                       validationState={fieldState.error ? 'invalid' : 'valid'}>
+                <Field label={argLabel(arg)} width="100%" errorMessage={fieldState.error ? fieldState.error.message : undefined} validationState={fieldState.error ? 'invalid' : 'valid'}>
                   <div>
                     <ListView
                       {...field}
@@ -257,7 +220,7 @@ const CodeArgumentInput: React.FC<CodeArgumentInputProps> = ({arg}) => {
           name={arg.name}
           control={control}
           rules={controllerRules(arg)}
-          render={({field, fieldState}) => (
+          render={({ field, fieldState }) => (
             <View key={arg.name} marginBottom="size-200">
               <NumberField
                 {...field}
@@ -267,7 +230,7 @@ const CodeArgumentInput: React.FC<CodeArgumentInputProps> = ({arg}) => {
                 minValue={arg.min !== null ? arg.min : undefined}
                 maxValue={arg.max !== null ? arg.max : undefined}
                 hideStepper={arg.type === 'DECIMAL'}
-                formatOptions={arg.type === 'INTEGER' ? {maximumFractionDigits: 0} : undefined}
+                formatOptions={arg.type === 'INTEGER' ? { maximumFractionDigits: 0 } : undefined}
                 aria-label={`Argument '${arg.name}'`}
               />
             </View>

From f3781b482e2121629ce79a0a9178cebc5a9db7e9 Mon Sep 17 00:00:00 2001
From: "kamil.orwat" <kamil.orwat@vml.com>
Date: Mon, 28 Apr 2025 09:22:00 +0200
Subject: [PATCH 6/7] Updated error message

---
 .../java/com/vml/es/aem/acm/core/code/arg/DateTimeArgument.java | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/core/src/main/java/com/vml/es/aem/acm/core/code/arg/DateTimeArgument.java b/core/src/main/java/com/vml/es/aem/acm/core/code/arg/DateTimeArgument.java
index 2fca0715..8f497575 100644
--- a/core/src/main/java/com/vml/es/aem/acm/core/code/arg/DateTimeArgument.java
+++ b/core/src/main/java/com/vml/es/aem/acm/core/code/arg/DateTimeArgument.java
@@ -75,7 +75,7 @@ public static DateTimeArgument.Variant of(String name) {
                     .filter(r -> r.name().equalsIgnoreCase(name))
                     .findFirst()
                     .orElseThrow(() -> new IllegalArgumentException(
-                            String.format("Datetime argument cannot be displayed as '%s'!", name)));
+                            String.format("Datetime variant '%s' is not supported!", name)));
         }
     }
 }

From dd39b200697b2a959b3bb8d51ce9bc563531a17a Mon Sep 17 00:00:00 2001
From: "kamil.orwat" <kamil.orwat@vml.com>
Date: Mon, 28 Apr 2025 09:39:30 +0200
Subject: [PATCH 7/7] added private package in pomxml

---
 pom.xml | 1 +
 1 file changed, 1 insertion(+)

diff --git a/pom.xml b/pom.xml
index 21df35af..a8b67ae1 100644
--- a/pom.xml
+++ b/pom.xml
@@ -233,6 +233,7 @@ Bundle-Category: acm
 Bundle-DocURL:
 -plugin org.apache.sling.caconfig.bndplugin.ConfigurationClassScannerPlugin
 -plugin org.apache.sling.bnd.models.ModelsScannerPlugin
+Private-Package: com.fasterxml.jackson.datatype.jsr310
                                 ]]>
                                 </bnd>
                             </configuration>