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>