From 9666c0e4414e316ad2128e66bc2369eba115aca2 Mon Sep 17 00:00:00 2001 From: awildturtok <1553491+awildturtok@users.noreply.github.com> Date: Mon, 4 Sep 2023 16:22:28 +0200 Subject: [PATCH 01/47] remove easily broken trace logging --- .../conquery/io/mina/BinaryJacksonCoder.java | 29 +++++-------------- 1 file changed, 8 insertions(+), 21 deletions(-) diff --git a/backend/src/main/java/com/bakdata/conquery/io/mina/BinaryJacksonCoder.java b/backend/src/main/java/com/bakdata/conquery/io/mina/BinaryJacksonCoder.java index d71b715036..130d05f1ee 100644 --- a/backend/src/main/java/com/bakdata/conquery/io/mina/BinaryJacksonCoder.java +++ b/backend/src/main/java/com/bakdata/conquery/io/mina/BinaryJacksonCoder.java @@ -1,11 +1,7 @@ package com.bakdata.conquery.io.mina; -import java.io.File; -import java.util.UUID; - import javax.validation.Validator; -import com.bakdata.conquery.io.jackson.Jackson; import com.bakdata.conquery.models.exceptions.ValidatorHelper; import com.bakdata.conquery.models.messages.network.NetworkMessage; import com.bakdata.conquery.models.worker.IdResolveContext; @@ -14,7 +10,6 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectReader; import com.fasterxml.jackson.databind.ObjectWriter; -import com.fasterxml.jackson.databind.SerializationFeature; import lombok.extern.slf4j.Slf4j; @Slf4j @@ -26,34 +21,26 @@ public class BinaryJacksonCoder implements CQCoder> { public BinaryJacksonCoder(IdResolveContext datasets, Validator validator, ObjectMapper objectMapper) { this.validator = validator; - this.writer = objectMapper - .writerFor(NetworkMessage.class); - this.reader = datasets - .injectIntoNew(objectMapper.readerFor(NetworkMessage.class)) - .without(Feature.AUTO_CLOSE_SOURCE); + writer = objectMapper.writerFor(NetworkMessage.class); + reader = datasets.injectIntoNew(objectMapper.readerFor(NetworkMessage.class)).without(Feature.AUTO_CLOSE_SOURCE); } @Override public Chunkable encode(NetworkMessage message) throws Exception { ValidatorHelper.failOnError(log, validator.validate(message)); - UUID id = message.getMessageId(); - Chunkable chunkable = new Chunkable(id, writer, message); - if(log.isTraceEnabled()) { - Jackson.MAPPER.writerFor(NetworkMessage.class).with(SerializationFeature.INDENT_OUTPUT).writeValue(new File("dumps/out_"+id+".json"), message); - } - return chunkable; + return new Chunkable(message.getMessageId(), writer, message); } @Override public NetworkMessage decode(ChunkedMessage message) throws Exception { - try(EndCheckableInputStream is = message.createInputStream()) { - Object obj = reader.readValue(is); - if(!is.isAtEnd()) { - throw new IllegalStateException("After reading the JSON message "+obj+" the buffer has still bytes available"); + try (EndCheckableInputStream is = message.createInputStream()) { + final Object obj = reader.readValue(is); + if (!is.isAtEnd()) { + throw new IllegalStateException("After reading the JSON message " + obj + " the buffer has still bytes available"); } ValidatorHelper.failOnError(log, validator.validate(obj)); - return (NetworkMessage)obj; + return (NetworkMessage) obj; } } } From 92d8adbe47f376923d33a81ea3f3510dec7784f2 Mon Sep 17 00:00:00 2001 From: awildturtok <1553491+awildturtok@users.noreply.github.com> Date: Mon, 4 Sep 2023 16:59:22 +0200 Subject: [PATCH 02/47] compute containsDates at submission, or fullOverview, but never juts overview --- .../apiv1/execution/ExecutionStatus.java | 2 ++ .../apiv1/execution/FullExecutionStatus.java | 1 - .../models/execution/ManagedExecution.java | 19 +++++++++++++------ .../conquery/models/query/ManagedQuery.java | 12 +++++------- 4 files changed, 20 insertions(+), 14 deletions(-) diff --git a/backend/src/main/java/com/bakdata/conquery/apiv1/execution/ExecutionStatus.java b/backend/src/main/java/com/bakdata/conquery/apiv1/execution/ExecutionStatus.java index 4aec080f22..51f83c56f6 100644 --- a/backend/src/main/java/com/bakdata/conquery/apiv1/execution/ExecutionStatus.java +++ b/backend/src/main/java/com/bakdata/conquery/apiv1/execution/ExecutionStatus.java @@ -44,6 +44,8 @@ public abstract class ExecutionStatus { private String queryType; private SecondaryIdDescriptionId secondaryId; + private boolean containsDates; + /** * The urls under from which the result of the execution can be downloaded as soon as it finished successfully. diff --git a/backend/src/main/java/com/bakdata/conquery/apiv1/execution/FullExecutionStatus.java b/backend/src/main/java/com/bakdata/conquery/apiv1/execution/FullExecutionStatus.java index 32d9d45c38..2984e904e0 100644 --- a/backend/src/main/java/com/bakdata/conquery/apiv1/execution/FullExecutionStatus.java +++ b/backend/src/main/java/com/bakdata/conquery/apiv1/execution/FullExecutionStatus.java @@ -46,7 +46,6 @@ public class FullExecutionStatus extends ExecutionStatus { */ private boolean canExpand; - private boolean containsDates; /** * Is set to the query description if the user can expand all included concepts. diff --git a/backend/src/main/java/com/bakdata/conquery/models/execution/ManagedExecution.java b/backend/src/main/java/com/bakdata/conquery/models/execution/ManagedExecution.java index fb709ad704..c29184634f 100644 --- a/backend/src/main/java/com/bakdata/conquery/models/execution/ManagedExecution.java +++ b/backend/src/main/java/com/bakdata/conquery/models/execution/ManagedExecution.java @@ -94,6 +94,8 @@ public abstract class ManagedExecution extends IdentifiableImpl { if (visitable instanceof CQConcept cqConcept) { @@ -373,7 +381,7 @@ private boolean containsDates(QueryDescription query) { }); } - private boolean canSubjectExpand(Subject subject, QueryDescription query) { + private static boolean canSubjectExpand(Subject subject, QueryDescription query) { NamespacedIdentifiableCollector namespacesIdCollector = new NamespacedIdentifiableCollector(); query.visit(namespacesIdCollector); @@ -401,9 +409,8 @@ public boolean isReadyToDownload() { @JsonIgnore public String getLabelWithoutAutoLabelSuffix() { - int idx; + final int idx; if (label != null && (idx = label.lastIndexOf(AUTO_LABEL_SUFFIX)) != -1) { - return label.substring(0, idx); } return label; diff --git a/backend/src/main/java/com/bakdata/conquery/models/query/ManagedQuery.java b/backend/src/main/java/com/bakdata/conquery/models/query/ManagedQuery.java index 37cc0e5e63..46da29ffe1 100644 --- a/backend/src/main/java/com/bakdata/conquery/models/query/ManagedQuery.java +++ b/backend/src/main/java/com/bakdata/conquery/models/query/ManagedQuery.java @@ -61,12 +61,12 @@ public class ManagedQuery extends ManagedExecution implements SingleTableResult, */ private Long lastResultCount; - //TODO this can actually be known ahead and reduced to speedup queries. @JsonIgnore private transient Set involvedWorkers; @JsonIgnore private transient List columnDescriptions; + protected ManagedQuery(@JacksonInject(useInput = OptBoolean.FALSE) MetaStorage storage) { super(storage); } @@ -78,9 +78,7 @@ public ManagedQuery(Query query, User owner, Dataset submittedDataset, MetaStora @Override protected void doInitExecutable() { - query.resolve(new QueryResolveContext(getNamespace(), getConfig(), getStorage(), null)); - } @Override @@ -154,15 +152,15 @@ protected void setAdditionalFieldsForStatusWithColumnDescription(Subject subject */ public List generateColumnDescriptions() { Preconditions.checkArgument(isInitialized(), "The execution must have been initialized first"); - List columnDescriptions = new ArrayList<>(); + final List columnDescriptions = new ArrayList<>(); final Locale locale = I18n.LOCALE.get(); - PrintSettings settings = new PrintSettings(true, locale, getNamespace(), getConfig(), null); + final PrintSettings settings = new PrintSettings(true, locale, getNamespace(), getConfig(), null); - UniqueNamer uniqNamer = new UniqueNamer(settings); + final UniqueNamer uniqNamer = new UniqueNamer(settings); - // First add the id columns to the descriptor list. The are the first columns + // First add the id columns to the descriptor list. These are always the first columns for (ResultInfo header : getConfig().getIdColumns().getIdResultInfos()) { columnDescriptions.add(ColumnDescriptor.builder() .label(uniqNamer.getUniqueName(header)) From a593c2e003cbbe0e34175ceb2565be61db120b75 Mon Sep 17 00:00:00 2001 From: Marco Korinth Date: Thu, 31 Aug 2023 14:06:17 +0200 Subject: [PATCH 03/47] fix: datepicker blocks input field --- frontend/src/js/common/helpers/mergeRefs.ts | 17 ++++ .../ui-components/InputDate/CustomHeader.tsx | 2 +- .../js/ui-components/InputDate/InputDate.tsx | 84 +++++++++---------- .../src/js/ui-components/InputDateRange.tsx | 5 +- 4 files changed, 59 insertions(+), 49 deletions(-) create mode 100644 frontend/src/js/common/helpers/mergeRefs.ts diff --git a/frontend/src/js/common/helpers/mergeRefs.ts b/frontend/src/js/common/helpers/mergeRefs.ts new file mode 100644 index 0000000000..20b0e38f17 --- /dev/null +++ b/frontend/src/js/common/helpers/mergeRefs.ts @@ -0,0 +1,17 @@ +type Mutable = { + -readonly [k in keyof T]: T[k]; +}; + +export const mergeRefs = + (...refs: React.Ref[]) => + (value: T): void => { + for (let i = 0; i < refs.length; i += 1) { + const ref = refs[i]; + + if (typeof ref === "function") { + ref(value); + } else if (ref) { + (ref as Mutable>).current = value; + } + } + }; diff --git a/frontend/src/js/ui-components/InputDate/CustomHeader.tsx b/frontend/src/js/ui-components/InputDate/CustomHeader.tsx index 83b0b594c0..310df8480b 100644 --- a/frontend/src/js/ui-components/InputDate/CustomHeader.tsx +++ b/frontend/src/js/ui-components/InputDate/CustomHeader.tsx @@ -102,7 +102,7 @@ const YearMonthSelect = ({ value: i, })); - const [yearSelectOpen, setYearSelectOpen] = useState(false); + const [yearSelectOpen, setYearSelectOpen] = useState(true); const [monthSelectOpen, setMonthSelectOpen] = useState(false); const handleClick = () => { if (yearSelectOpen || monthSelectOpen) { diff --git a/frontend/src/js/ui-components/InputDate/InputDate.tsx b/frontend/src/js/ui-components/InputDate/InputDate.tsx index 00e57bc56b..7b4d987856 100644 --- a/frontend/src/js/ui-components/InputDate/InputDate.tsx +++ b/frontend/src/js/ui-components/InputDate/InputDate.tsx @@ -1,9 +1,12 @@ import styled from "@emotion/styled"; -import { createElement, forwardRef, useRef, useState } from "react"; +import { faCalendar } from "@fortawesome/free-regular-svg-icons"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { createElement, forwardRef, useRef } from "react"; import ReactDatePicker from "react-datepicker"; import "react-datepicker/dist/react-datepicker.css"; import { formatDate, parseDate } from "../../common/helpers/dateHelper"; +import { mergeRefs } from "../../common/helpers/mergeRefs"; import BaseInput, { Props as BaseInputProps } from "../BaseInput"; import { CustomHeader } from "./CustomHeader"; @@ -19,11 +22,25 @@ const Root = styled("div")` } .react-datepicker-popper[data-placement^="bottom"] { padding-top: 4px; - transform: translate3d(0, 32px, 0) !important; } .react-datepicker-popper[data-placement^="top"] { padding-bottom: 0; - transform: translate3d(0, 32px, 0) !important; + } +`; + +const CalendarIcon = styled(FontAwesomeIcon)` + position: absolute; + width: 16px; + height: 16px; + top: calc(50% - 8px); + left: 5px; + cursor: pointer; + color: ${({ theme }) => theme.col.black}; +`; + +const StyledBaseInput = styled(BaseInput)` + input { + padding-left: 22px; } `; @@ -45,28 +62,12 @@ type Props = Omit & { onCalendarSelect?: (val: string) => void; }; -// TODO: Remove this once we have solved -// - that the date picker overlays other fields in forms -const TEMPORARILY_DISABLED_DATE_PICKER = true; - -const InputDate = forwardRef( +const InputDate = forwardRef( ( - { - className, - value, - dateFormat, - onChange, - onCalendarSelect, - onFocus, - onBlur, - onClick, - ...props - }, + { className, value, dateFormat, onChange, onCalendarSelect, ...props }, ref, ) => { const datePickerRef = useRef(null); - const [hasFocus, setHasFocus] = useState(false); - const [focusBlocked, setFocusBlocked] = useState(false); return ( ( if (e.key === "Escape") datePickerRef.current?.setOpen(false); }} > - { onChange(val as string); }} - onFocus={(e) => { - if (focusBlocked) { - e.currentTarget.blur(); - setFocusBlocked(false); - } else { - onFocus?.(e); - setHasFocus(true); - datePickerRef.current?.setOpen(true); - } - }} - onBlur={(e) => { - onBlur?.(e); - setHasFocus(false); - }} - onClick={(e) => { - onClick?.(e); - if (hasFocus) { - datePickerRef.current?.setOpen(true); - } - }} inputProps={{ ...props?.inputProps, onKeyPress: (e) => { @@ -111,8 +91,12 @@ const InputDate = forwardRef( }, }} /> + datePickerRef.current?.setOpen(true)} + /> { if (!val) { @@ -122,7 +106,6 @@ const InputDate = forwardRef( const selectedDate = formatDate(val, dateFormat); onChange(selectedDate); onCalendarSelect?.(selectedDate); - setFocusBlocked(true); datePickerRef.current?.setOpen(false); }} onClickOutside={() => datePickerRef.current?.setOpen(false)} @@ -130,7 +113,16 @@ const InputDate = forwardRef( customInput={createElement(HiddenInput)} calendarContainer={StyledCalendar} calendarStartDay={1} - disabled={TEMPORARILY_DISABLED_DATE_PICKER} + popperProps={{ + modifiers: [ + { + name: "preventOverflow", + options: { + mainAxis: false, + }, + }, + ], + }} /> ); diff --git a/frontend/src/js/ui-components/InputDateRange.tsx b/frontend/src/js/ui-components/InputDateRange.tsx index e58a00745e..33d2bdb747 100644 --- a/frontend/src/js/ui-components/InputDateRange.tsx +++ b/frontend/src/js/ui-components/InputDateRange.tsx @@ -2,6 +2,7 @@ import { css } from "@emotion/react"; import styled from "@emotion/styled"; import { faCalendar } from "@fortawesome/free-regular-svg-icons"; import { FC, ReactNode, createRef, useMemo } from "react"; +import ReactDatePicker from "react-datepicker"; import { useTranslation } from "react-i18next"; import { IndexPrefix } from "../common/components/IndexPrefix"; @@ -166,7 +167,7 @@ const InputDateRange: FC = ({ const min = getDisplayDate("min", value, displayDateFormat); const max = getDisplayDate("max", value, displayDateFormat); - const maxRef = createRef(); + const maxRef = createRef(); const isMinValid = exists(value.min && parseDate(min, displayDateFormat)); const isMaxValid = exists(value.max && parseDate(max, displayDateFormat)); @@ -213,7 +214,7 @@ const InputDateRange: FC = ({ onChange={(val) => onChangeRaw("min", val as string, displayDateFormat) } - onCalendarSelect={() => maxRef.current?.focus()} + onCalendarSelect={() => maxRef.current?.setOpen(true)} onBlur={(e) => applyDate("min", e.target.value, displayDateFormat)} inputProps={{ autoFocus, From 383f61b8960216e599c540e94e21e9ecc3c64dec Mon Sep 17 00:00:00 2001 From: Marco Korinth Date: Tue, 5 Sep 2023 08:00:58 +0200 Subject: [PATCH 04/47] refactor: use IconButton for Datepicker icon and implement mergeRefs dependency --- frontend/package.json | 1 + frontend/src/js/common/helpers/mergeRefs.ts | 17 ----------------- .../js/ui-components/InputDate/InputDate.tsx | 14 +++++++------- frontend/yarn.lock | 5 +++++ 4 files changed, 13 insertions(+), 24 deletions(-) delete mode 100644 frontend/src/js/common/helpers/mergeRefs.ts diff --git a/frontend/package.json b/frontend/package.json index 742c47ff5f..5bef82954a 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -69,6 +69,7 @@ "react-i18next": "^12.2.0", "react-list": "^0.8.16", "react-markdown": "^8.0.0", + "react-merge-refs": "^2.0.2", "react-number-format": "^5.1.4", "react-redux": "^8.0.5", "react-router-dom": "^6.9.0", diff --git a/frontend/src/js/common/helpers/mergeRefs.ts b/frontend/src/js/common/helpers/mergeRefs.ts deleted file mode 100644 index 20b0e38f17..0000000000 --- a/frontend/src/js/common/helpers/mergeRefs.ts +++ /dev/null @@ -1,17 +0,0 @@ -type Mutable = { - -readonly [k in keyof T]: T[k]; -}; - -export const mergeRefs = - (...refs: React.Ref[]) => - (value: T): void => { - for (let i = 0; i < refs.length; i += 1) { - const ref = refs[i]; - - if (typeof ref === "function") { - ref(value); - } else if (ref) { - (ref as Mutable>).current = value; - } - } - }; diff --git a/frontend/src/js/ui-components/InputDate/InputDate.tsx b/frontend/src/js/ui-components/InputDate/InputDate.tsx index 7b4d987856..950ceac5c7 100644 --- a/frontend/src/js/ui-components/InputDate/InputDate.tsx +++ b/frontend/src/js/ui-components/InputDate/InputDate.tsx @@ -1,12 +1,12 @@ import styled from "@emotion/styled"; import { faCalendar } from "@fortawesome/free-regular-svg-icons"; -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { createElement, forwardRef, useRef } from "react"; import ReactDatePicker from "react-datepicker"; import "react-datepicker/dist/react-datepicker.css"; +import { mergeRefs } from "react-merge-refs"; +import IconButton from "../../button/IconButton"; import { formatDate, parseDate } from "../../common/helpers/dateHelper"; -import { mergeRefs } from "../../common/helpers/mergeRefs"; import BaseInput, { Props as BaseInputProps } from "../BaseInput"; import { CustomHeader } from "./CustomHeader"; @@ -28,19 +28,18 @@ const Root = styled("div")` } `; -const CalendarIcon = styled(FontAwesomeIcon)` +const CalendarIcon = styled(IconButton)` position: absolute; width: 16px; height: 16px; top: calc(50% - 8px); left: 5px; - cursor: pointer; - color: ${({ theme }) => theme.col.black}; + padding: 0; `; const StyledBaseInput = styled(BaseInput)` input { - padding-left: 22px; + padding-left: 28px; } `; @@ -93,10 +92,11 @@ const InputDate = forwardRef( /> datePickerRef.current?.setOpen(true)} /> { if (!val) { diff --git a/frontend/yarn.lock b/frontend/yarn.lock index 10880d182f..d65beaf493 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -8622,6 +8622,11 @@ react-markdown@^8.0.0: unist-util-visit "^4.0.0" vfile "^5.0.0" +react-merge-refs@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/react-merge-refs/-/react-merge-refs-2.0.2.tgz#73f576111124897dec4ea56035a97e199e8cb377" + integrity sha512-V5BGTwGa2r+/t0A/BZMS6L7VPXY0CU8xtAhkT3XUoI1WJJhhtvulvoiZkJ5Jt9YAW23m4xFWmhQ+C5HwjtTFhQ== + react-number-format@^5.1.4: version "5.1.4" resolved "https://registry.yarnpkg.com/react-number-format/-/react-number-format-5.1.4.tgz#23057d94a4f1b08e12ee41328e86be929b60a791" From fbc52dd996d0194b11c1bedf96594037c3165065 Mon Sep 17 00:00:00 2001 From: awildturtok <1553491+awildturtok@users.noreply.github.com> Date: Tue, 5 Sep 2023 10:53:32 +0200 Subject: [PATCH 05/47] cleanup Shareable code --- .../conquery/models/execution/Shareable.java | 45 +++++++++---------- 1 file changed, 22 insertions(+), 23 deletions(-) diff --git a/backend/src/main/java/com/bakdata/conquery/models/execution/Shareable.java b/backend/src/main/java/com/bakdata/conquery/models/execution/Shareable.java index 922fdf81ef..b626b88ddb 100644 --- a/backend/src/main/java/com/bakdata/conquery/models/execution/Shareable.java +++ b/backend/src/main/java/com/bakdata/conquery/models/execution/Shareable.java @@ -31,42 +31,41 @@ public interface Shareable extends Authorized { */ void setShared(boolean shared); - default , S extends Identifiable & Shareable & Authorized> Consumer sharer( - MetaStorage storage, - Subject subject) { + default , S extends Identifiable & Shareable & Authorized> Consumer sharer(MetaStorage storage, Subject subject) { if (!(this instanceof Identifiable)) { log.warn("Cannot share {} ({}) because it does not implement Identifiable", this.getClass(), this.toString()); return QueryUtils.getNoOpEntryPoint(); } - return (patch) -> { - if (patch != null && patch.getGroups() != null) { - S shareable = (S) this; - // Collect groups that do not have access to this instance and remove their probable permission - for (Group group1 : storage.getAllGroups()) { - if (patch.getGroups().contains(group1.getId())) { - continue; - } - log.trace("User {} unshares instance {} ({}) from owner {}.", subject, shareable.getClass().getSimpleName(), shareable.getId(), group1); + return (patch) -> { + if (patch == null || patch.getGroups() == null) { + return; + } - group1.removePermission(shareable.createPermission(AbilitySets.SHAREHOLDER)); + final S shareable = (S) this; + // Collect groups that do not have access to this instance and remove their probable permission + for (Group group : storage.getAllGroups()) { + if (patch.getGroups().contains(group.getId())) { + continue; } + log.trace("User {} unshares instance {} ({}) from owner {}.", subject, shareable.getClass().getSimpleName(), shareable.getId(), group); - if(!patch.getGroups().isEmpty()) { - // Resolve the provided groups - Set groups = patch.getGroups().stream().map(storage::getGroup).collect(Collectors.toSet()); + group.removePermission(shareable.createPermission(AbilitySets.SHAREHOLDER)); + } - for(Group group : groups) { - ConqueryPermission sharePermission = shareable.createPermission(AbilitySets.SHAREHOLDER); - group.addPermission(sharePermission); - log.trace("User {} shares instance {} ({}). Adding permission {} to owner {}.", subject, shareable.getClass().getSimpleName(), shareable.getId(), sharePermission, group); - } - } + // Resolve the provided groups + final Set groups = patch.getGroups().stream().map(storage::getGroup).collect(Collectors.toSet()); + + for(Group group : groups) { + final ConqueryPermission sharePermission = shareable.createPermission(AbilitySets.SHAREHOLDER); + group.addPermission(sharePermission); - this.setShared(!patch.getGroups().isEmpty()); + log.trace("User {} shares instance {} ({}). Adding permission {} to owner {}.", subject, shareable.getClass().getSimpleName(), shareable.getId(), sharePermission, group); } + + setShared(!patch.getGroups().isEmpty()); }; } From 293ce64020a7aac42e6f0e34f75478dcd8c3e109 Mon Sep 17 00:00:00 2001 From: awildturtok <1553491+awildturtok@users.noreply.github.com> Date: Tue, 5 Sep 2023 11:12:02 +0200 Subject: [PATCH 06/47] Share sub-executions when sharing executions --- .../bakdata/conquery/apiv1/QueryProcessor.java | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/backend/src/main/java/com/bakdata/conquery/apiv1/QueryProcessor.java b/backend/src/main/java/com/bakdata/conquery/apiv1/QueryProcessor.java index 119650f5dc..bd94e84acc 100644 --- a/backend/src/main/java/com/bakdata/conquery/apiv1/QueryProcessor.java +++ b/backend/src/main/java/com/bakdata/conquery/apiv1/QueryProcessor.java @@ -312,6 +312,21 @@ public void patchQuery(Subject subject, ManagedExecution execution, MetaDataPatc log.info("Patching {} ({}) with patch: {}", execution.getClass().getSimpleName(), execution, patch); + // If the patch shares the execution, we also share all subQueries + if (patch.getGroups() != null && !patch.getGroups().isEmpty()) { + final MetaDataPatch sharePatch = MetaDataPatch.builder() + .groups(patch.getGroups()) + .build(); + + for (ManagedExecutionId managedExecutionId : execution.getSubmitted().collectRequiredQueries()) { + final ManagedExecution subQuery = storage.getExecution(managedExecutionId); + + subject.authorize(subQuery, Ability.READ); + + patchQuery(subject, subQuery, sharePatch); + } + } + patch.applyTo(execution, storage, subject); storage.updateExecution(execution); } From 2f35c67598322ce9920cc4a8c82921af43e4661b Mon Sep 17 00:00:00 2001 From: Kai Rollmann Date: Tue, 5 Sep 2023 13:49:45 +0200 Subject: [PATCH 07/47] Make all non-required field errors red --- frontend/src/js/external-forms/form/Field.tsx | 31 ++++++++++++++----- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/frontend/src/js/external-forms/form/Field.tsx b/frontend/src/js/external-forms/form/Field.tsx index ad657d4e2f..7741bd2f38 100644 --- a/frontend/src/js/external-forms/form/Field.tsx +++ b/frontend/src/js/external-forms/form/Field.tsx @@ -73,7 +73,11 @@ type Props = T & { noContainer?: boolean; noLabel?: boolean; }; -const FieldContainer = styled("div")<{ noLabel?: boolean; hasError?: boolean }>` +const FieldContainer = styled("div")<{ + noLabel?: boolean; + hasError?: boolean; + red?: boolean; +}>` display: flex; flex-direction: column; gap: 5px; @@ -81,12 +85,16 @@ const FieldContainer = styled("div")<{ noLabel?: boolean; hasError?: boolean }>` background-color: white; border-radius: ${({ theme }) => theme.borderRadius}; border: 1px solid - ${({ theme, hasError }) => - hasError ? theme.col.blueGrayDark : theme.col.grayLight}; + ${({ theme, hasError, red }) => + hasError + ? red + ? theme.col.red + : theme.col.blueGrayDark + : theme.col.grayLight}; `; -const ErrorContainer = styled("div")` - color: ${({ theme }) => theme.col.blueGrayDark}; +const ErrorContainer = styled("div")<{ red?: boolean }>` + color: ${({ theme, red }) => (red ? theme.col.red : theme.col.blueGrayDark)}; font-weight: 700; font-size: ${({ theme }) => theme.font.sm}; `; @@ -114,12 +122,21 @@ const ConnectedField = ({ // TODO: REFINE COLORS // const color = useColorByField(formField.type); + const requiredMsg = t("externalForms.formValidation.isRequired"); + const isRedError = fieldState.error?.message !== requiredMsg; + return noContainer ? (
{children({ ...field, ...props })}
) : ( - + {children({ ...field, ...props })} - {fieldState.error?.message} + + {fieldState.error?.message} + ); }; From ddd85b1d275c8491a4c9964eb45e23e9b8133d62 Mon Sep 17 00:00:00 2001 From: awildturtok <1553491+awildturtok@users.noreply.github.com> Date: Tue, 5 Sep 2023 15:28:29 +0200 Subject: [PATCH 08/47] find groups execution is already shared with --- .../conquery/apiv1/QueryProcessor.java | 34 ++++++++++++++++--- 1 file changed, 30 insertions(+), 4 deletions(-) diff --git a/backend/src/main/java/com/bakdata/conquery/apiv1/QueryProcessor.java b/backend/src/main/java/com/bakdata/conquery/apiv1/QueryProcessor.java index bd94e84acc..2f38a056ae 100644 --- a/backend/src/main/java/com/bakdata/conquery/apiv1/QueryProcessor.java +++ b/backend/src/main/java/com/bakdata/conquery/apiv1/QueryProcessor.java @@ -6,10 +6,12 @@ import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Optional; +import java.util.Set; import java.util.concurrent.TimeUnit; import java.util.function.Consumer; import java.util.function.Predicate; @@ -48,6 +50,7 @@ import com.bakdata.conquery.models.auth.entities.Subject; import com.bakdata.conquery.models.auth.entities.User; import com.bakdata.conquery.models.auth.permissions.Ability; +import com.bakdata.conquery.models.auth.permissions.ConqueryPermission; import com.bakdata.conquery.models.common.Range; import com.bakdata.conquery.models.config.ColumnConfig; import com.bakdata.conquery.models.config.ConqueryConfig; @@ -58,6 +61,7 @@ import com.bakdata.conquery.models.error.ConqueryError; import com.bakdata.conquery.models.execution.ExecutionState; import com.bakdata.conquery.models.execution.ManagedExecution; +import com.bakdata.conquery.models.identifiable.ids.specific.GroupId; import com.bakdata.conquery.models.identifiable.ids.specific.ManagedExecutionId; import com.bakdata.conquery.models.identifiable.mapping.IdPrinter; import com.bakdata.conquery.models.query.ExecutionManager; @@ -314,14 +318,36 @@ public void patchQuery(Subject subject, ManagedExecution execution, MetaDataPatc // If the patch shares the execution, we also share all subQueries if (patch.getGroups() != null && !patch.getGroups().isEmpty()) { - final MetaDataPatch sharePatch = MetaDataPatch.builder() - .groups(patch.getGroups()) - .build(); + for (ManagedExecutionId managedExecutionId : execution.getSubmitted().collectRequiredQueries()) { final ManagedExecution subQuery = storage.getExecution(managedExecutionId); - subject.authorize(subQuery, Ability.READ); + if (!subject.isPermitted(subQuery, Ability.READ)) { + log.warn("Not sharing {} as User {} is not allowed to see it themselves.", subQuery.getId(), subject); + continue; + } + + final ConqueryPermission canReadQuery = subQuery.createPermission(Set.of(Ability.READ)); + + final Set groupsToShareWith = new HashSet<>(patch.getGroups()); + + // Find all groups the query is already shared with, so we do not remove them, as patch is absolute + for (Group group : storage.getAllGroups()) { + if (groupsToShareWith.contains(group.getId())){ + continue; + } + + final Set effectivePermissions = group.getEffectivePermissions(); + + if(effectivePermissions.stream().anyMatch(perm -> perm.implies(canReadQuery))) { + groupsToShareWith.add(group.getId()); + } + } + + final MetaDataPatch sharePatch = MetaDataPatch.builder() + .groups(new ArrayList<>(groupsToShareWith)) + .build(); patchQuery(subject, subQuery, sharePatch); } From e7d133b846d17d6b0965d207218c7fd2ab2a6e5e Mon Sep 17 00:00:00 2001 From: Kai Rollmann Date: Tue, 5 Sep 2023 10:19:21 +0200 Subject: [PATCH 09/47] Increase clickable area of Calendar Button and center it --- .../src/js/ui-components/InputDate/InputDate.tsx | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/frontend/src/js/ui-components/InputDate/InputDate.tsx b/frontend/src/js/ui-components/InputDate/InputDate.tsx index 950ceac5c7..6d035fc919 100644 --- a/frontend/src/js/ui-components/InputDate/InputDate.tsx +++ b/frontend/src/js/ui-components/InputDate/InputDate.tsx @@ -28,13 +28,11 @@ const Root = styled("div")` } `; -const CalendarIcon = styled(IconButton)` +const CalendarButton = styled(IconButton)` position: absolute; - width: 16px; - height: 16px; - top: calc(50% - 8px); - left: 5px; - padding: 0; + left: 0; + top: 0; + padding: 8px 10px; `; const StyledBaseInput = styled(BaseInput)` @@ -90,9 +88,8 @@ const InputDate = forwardRef( }, }} /> - datePickerRef.current?.setOpen(true)} /> Date: Wed, 6 Sep 2023 11:41:32 +0200 Subject: [PATCH 10/47] Reduce ChunkWriter#bufferSize to 2MB as 32MB is a massive waste of Memory --- .../src/main/java/com/bakdata/conquery/io/mina/ChunkWriter.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/main/java/com/bakdata/conquery/io/mina/ChunkWriter.java b/backend/src/main/java/com/bakdata/conquery/io/mina/ChunkWriter.java index fdf9095dbe..44d1cd462d 100644 --- a/backend/src/main/java/com/bakdata/conquery/io/mina/ChunkWriter.java +++ b/backend/src/main/java/com/bakdata/conquery/io/mina/ChunkWriter.java @@ -26,7 +26,7 @@ public class ChunkWriter extends ProtocolEncoderAdapter { @Getter @Setter - private int bufferSize = Ints.checkedCast(Size.megabytes(32).toBytes()); + private int bufferSize = Ints.checkedCast(Size.megabytes(2).toBytes()); private final SoftPool bufferPool = new SoftPool<>(() -> IoBuffer.allocate(bufferSize)); @SuppressWarnings("rawtypes") private final CQCoder coder; From e4991f35f721687241755afabe061c8504e84cab Mon Sep 17 00:00:00 2001 From: Kai Rollmann Date: Mon, 11 Sep 2023 16:10:09 +0200 Subject: [PATCH 11/47] Add no dates in results indicator --- frontend/src/js/api/types.ts | 1 + .../js/previous-queries/list/ProjectItem.tsx | 45 ++++++++++++++----- .../list/ProjectItemDragContainer.tsx | 17 +++---- .../js/previous-queries/list/ProjectItems.tsx | 16 +++---- .../previous-queries/list/ProjectItemsTab.tsx | 4 +- .../src/js/previous-queries/list/reducer.ts | 1 + frontend/src/localization/de.json | 3 +- frontend/src/localization/en.json | 3 +- 8 files changed, 57 insertions(+), 33 deletions(-) diff --git a/frontend/src/js/api/types.ts b/frontend/src/js/api/types.ts index a9f45f45f8..83e76d48c2 100644 --- a/frontend/src/js/api/types.ts +++ b/frontend/src/js/api/types.ts @@ -414,6 +414,7 @@ export interface GetQueryResponseDoneT { columnDescriptions: ColumnDescription[] | null; queryType: "CONCEPT_QUERY" | "SECONDARY_ID_QUERY"; requiredTime: number; // In ms, unused at the moment + containsDates: boolean; } export interface GetQueryRunningResponseT { diff --git a/frontend/src/js/previous-queries/list/ProjectItem.tsx b/frontend/src/js/previous-queries/list/ProjectItem.tsx index b586985b12..a8bb14e9be 100644 --- a/frontend/src/js/previous-queries/list/ProjectItem.tsx +++ b/frontend/src/js/previous-queries/list/ProjectItem.tsx @@ -1,5 +1,8 @@ import styled from "@emotion/styled"; -import { faUser as faUserRegular } from "@fortawesome/free-regular-svg-icons"; +import { + faCalendar, + faUser as faUserRegular, +} from "@fortawesome/free-regular-svg-icons"; import { faFolder as faFolderRegular } from "@fortawesome/free-regular-svg-icons"; import { faFolder, @@ -18,6 +21,7 @@ import IconButton from "../../button/IconButton"; import { formatDate } from "../../common/helpers/dateHelper"; import { exists } from "../../common/helpers/exists"; import { useFormLabelByType } from "../../external-forms/stateSelectors"; +import FaIcon from "../../icon/FaIcon"; import FormSymbol from "../../symbols/FormSymbol"; import QuerySymbol from "../../symbols/QuerySymbol"; import WithTooltip from "../../tooltip/WithTooltip"; @@ -129,6 +133,16 @@ const SxDownloadButton = styled(DownloadButton)` } `; +const Row = styled("div")` + display: flex; + align-items: flex-start; + gap: 5px; +`; + +const SxFaIcon = styled(FaIcon)` + opacity: 0.7; +`; + const FoldersButton = styled(IconButton)` margin-right: 10px; `; @@ -220,17 +234,24 @@ const ProjectItem = forwardRef< /> {!isFormConfig(item) && item.resultUrls.length > 0 ? ( - - - {topLeftLabel} - - + + + + {topLeftLabel} + + + {!item.containsDates && ( + + + + )} + ) : ( {topLeftLabel} )} diff --git a/frontend/src/js/previous-queries/list/ProjectItemDragContainer.tsx b/frontend/src/js/previous-queries/list/ProjectItemDragContainer.tsx index 5e0d23b550..89bdbb8f8a 100644 --- a/frontend/src/js/previous-queries/list/ProjectItemDragContainer.tsx +++ b/frontend/src/js/previous-queries/list/ProjectItemDragContainer.tsx @@ -1,4 +1,4 @@ -import { FC, useRef } from "react"; +import { useRef } from "react"; import { useDrag } from "react-dnd"; import { getWidthAndHeight } from "../../app/DndProvider"; @@ -10,19 +10,20 @@ import ProjectItem, { ProjectItemT } from "./ProjectItem"; import { isFormConfig } from "./helpers"; import { PreviousQueryT } from "./reducer"; -interface PropsT { - item: ProjectItemT; - onIndicateShare: () => void; - onIndicateEditFolders: () => void; -} - const getDragType = (item: PreviousQueryT) => { return item.queryType === "CONCEPT_QUERY" ? DNDType.PREVIOUS_QUERY : DNDType.PREVIOUS_SECONDARY_ID_QUERY; }; -const ProjectItemDragContainer: FC = ({ item, ...props }) => { +const ProjectItemDragContainer = ({ + item, + ...props +}: { + item: ProjectItemT; + onIndicateShare: () => void; + onIndicateEditFolders: () => void; +}) => { const ref = useRef(null); const dragItemBase = { diff --git a/frontend/src/js/previous-queries/list/ProjectItems.tsx b/frontend/src/js/previous-queries/list/ProjectItems.tsx index 3e3652d0b7..64db88bce8 100644 --- a/frontend/src/js/previous-queries/list/ProjectItems.tsx +++ b/frontend/src/js/previous-queries/list/ProjectItems.tsx @@ -4,7 +4,6 @@ import { useState, useCallback, useLayoutEffect, - FC, useEffect, } from "react"; import { FixedSizeList } from "react-window"; @@ -17,11 +16,6 @@ import type { ProjectItemT } from "./ProjectItem"; import ProjectItemDragContainer from "./ProjectItemDragContainer"; import ShareProjectItemModal from "./ShareProjectItemModal"; -interface PropsT { - datasetId: DatasetT["id"] | null; - items: ProjectItemT[]; -} - const ROW_SIZE = 62; const ROOT_PADDING_Y = 4; @@ -31,7 +25,13 @@ const Root = styled("div")` padding: ${ROOT_PADDING_Y}px 0; `; -const ProjectItems: FC = ({ datasetId, items }) => { +export const ProjectItems = ({ + datasetId, + items, +}: { + items: ProjectItemT[]; + datasetId: DatasetT["id"] | null; +}) => { const [itemToShare, setItemToShare] = useState(null); const [itemToEditFolders, setItemToEditFolders] = useState(null); @@ -124,5 +124,3 @@ const ProjectItems: FC = ({ datasetId, items }) => { ); }; - -export default ProjectItems; diff --git a/frontend/src/js/previous-queries/list/ProjectItemsTab.tsx b/frontend/src/js/previous-queries/list/ProjectItemsTab.tsx index d7d5ffa657..737a530f42 100644 --- a/frontend/src/js/previous-queries/list/ProjectItemsTab.tsx +++ b/frontend/src/js/previous-queries/list/ProjectItemsTab.tsx @@ -22,7 +22,7 @@ import UploadQueryResults from "../upload/UploadQueryResults"; import Folders from "./Folders"; import FoldersToggleButton from "./FoldersToggleButton"; import { ProjectItemT } from "./ProjectItem"; -import PreviousQueries from "./ProjectItems"; +import { ProjectItems } from "./ProjectItems"; import { useLoadFormConfigs, useLoadQueries } from "./actions"; import type { FormConfigT, PreviousQueryT } from "./reducer"; import { selectPreviousQueries } from "./selector"; @@ -148,7 +148,7 @@ const ProjectItemsTab = ({ datasetId }: PropsT) => { )} - + diff --git a/frontend/src/js/previous-queries/list/reducer.ts b/frontend/src/js/previous-queries/list/reducer.ts index b7799584c4..a934f1e9ea 100644 --- a/frontend/src/js/previous-queries/list/reducer.ts +++ b/frontend/src/js/previous-queries/list/reducer.ts @@ -53,6 +53,7 @@ export interface PreviousQueryT { groups?: UserGroupIdT[]; queryType: "CONCEPT_QUERY" | "SECONDARY_ID_QUERY"; secondaryId?: string | null; + containsDates: boolean; } export interface PreviousQueriesStateT { diff --git a/frontend/src/localization/de.json b/frontend/src/localization/de.json index e5bbafb6ba..edb69201e0 100644 --- a/frontend/src/localization/de.json +++ b/frontend/src/localization/de.json @@ -231,7 +231,8 @@ "shareError": "Konnte Anfrage nicht freigeben", "deleteError": "Konnte Anfrage nicht löschen", "editFolders": "Ordner bearbeiten", - "deleteNow": "Anfrage jetzt löschen" + "deleteNow": "Anfrage jetzt löschen", + "hasNoDates": "Keine Datumswerte in den Ergebnissen vorhanden." }, "projectItemsFilter": { "all": "Alle", diff --git a/frontend/src/localization/en.json b/frontend/src/localization/en.json index be8312517c..42c2fea340 100644 --- a/frontend/src/localization/en.json +++ b/frontend/src/localization/en.json @@ -231,7 +231,8 @@ "shareError": "Could not share query", "deleteError": "Could not delete query", "editFolders": "Edit folders", - "deleteNow": "Delete query now" + "deleteNow": "Delete query now", + "hasNoDates": "No date values in the results." }, "projectItemsFilter": { "all": "All", From 4bc292a063847001f0f163430a60d850e39a3b0f Mon Sep 17 00:00:00 2001 From: Kai Rollmann Date: Mon, 11 Sep 2023 16:22:53 +0200 Subject: [PATCH 12/47] Stop showing whether a field is optional in all form fields --- .../form-components/DropzoneList.tsx | 10 +-- .../form-concept-group/FormConceptGroup.tsx | 2 - .../form-query-dropzone/FormQueryDropzone.tsx | 4 -- frontend/src/js/external-forms/form/Field.tsx | 68 +++++-------------- frontend/src/js/external-forms/form/Form.tsx | 4 +- frontend/src/js/external-forms/helper.ts | 9 --- .../src/js/ui-components/InputDateRange.tsx | 6 +- .../ui-components/InputPlain/InputPlain.tsx | 3 - .../ui-components/InputSelect/InputSelect.tsx | 3 - .../InputTextarea/InputTextarea.tsx | 13 +--- frontend/src/js/ui-components/Labeled.tsx | 4 -- frontend/src/js/ui-components/Optional.tsx | 18 ----- frontend/src/localization/de.json | 1 - frontend/src/localization/en.json | 1 - 14 files changed, 20 insertions(+), 126 deletions(-) delete mode 100644 frontend/src/js/ui-components/Optional.tsx diff --git a/frontend/src/js/external-forms/form-components/DropzoneList.tsx b/frontend/src/js/external-forms/form-components/DropzoneList.tsx index 953d16e1e2..304bfd07e3 100644 --- a/frontend/src/js/external-forms/form-components/DropzoneList.tsx +++ b/frontend/src/js/external-forms/form-components/DropzoneList.tsx @@ -13,7 +13,6 @@ import DropzoneWithFileInput, { DragItemFile, } from "../../ui-components/DropzoneWithFileInput"; import Label from "../../ui-components/Label"; -import Optional from "../../ui-components/Optional"; import DropzoneBetweenElements from "./DropzoneBetweenElements"; @@ -56,7 +55,6 @@ interface PropsT { className?: string; label?: ReactNode; tooltip?: string; - optional?: boolean; dropzoneChildren: (args: ChildArgs) => ReactNode; items: ReactNode[]; acceptedDropTypes: string[]; @@ -78,7 +76,6 @@ const DropzoneList = ( className, label, tooltip, - optional, dropzoneChildren, items, acceptedDropTypes, @@ -97,12 +94,7 @@ const DropzoneList = ( return (
- {label && ( - - )} + {label && } {tooltip && } {items && items.length > 0 && ( diff --git a/frontend/src/js/external-forms/form-concept-group/FormConceptGroup.tsx b/frontend/src/js/external-forms/form-concept-group/FormConceptGroup.tsx index 8821c30203..5b27a4c58b 100644 --- a/frontend/src/js/external-forms/form-concept-group/FormConceptGroup.tsx +++ b/frontend/src/js/external-forms/form-concept-group/FormConceptGroup.tsx @@ -70,7 +70,6 @@ interface Props { tooltip?: string; newValue: FormConceptGroupT; isSingle?: boolean; - optional?: boolean; disallowMultipleColumns?: boolean; blocklistedTables?: string[]; allowlistedTables?: string[]; @@ -189,7 +188,6 @@ const FormConceptGroup = (props: Props) => { */ ref={dropzoneRef} tooltip={props.tooltip} - optional={props.optional} label={ <> {props.label} diff --git a/frontend/src/js/external-forms/form-query-dropzone/FormQueryDropzone.tsx b/frontend/src/js/external-forms/form-query-dropzone/FormQueryDropzone.tsx index 831017a533..67a3dfbbe5 100644 --- a/frontend/src/js/external-forms/form-query-dropzone/FormQueryDropzone.tsx +++ b/frontend/src/js/external-forms/form-query-dropzone/FormQueryDropzone.tsx @@ -7,7 +7,6 @@ import type { DragItemQuery } from "../../standard-query-editor/types"; import InfoTooltip from "../../tooltip/InfoTooltip"; import Dropzone from "../../ui-components/Dropzone"; import Label from "../../ui-components/Label"; -import Optional from "../../ui-components/Optional"; import ValidatedFormQueryResult from "./ValidatedFormQueryResult"; @@ -23,7 +22,6 @@ const DROP_TYPES = [ interface PropsT { label: string; tooltip?: string; - optional?: boolean; dropzoneText: string; className?: string; value: DragItemQuery | null; @@ -33,7 +31,6 @@ interface PropsT { const FormQueryDropzone: FC = ({ label, tooltip, - optional, dropzoneText, className, value, @@ -60,7 +57,6 @@ const FormQueryDropzone: FC = ({ return (
diff --git a/frontend/src/js/external-forms/form/Field.tsx b/frontend/src/js/external-forms/form/Field.tsx index 7741bd2f38..81663890a5 100644 --- a/frontend/src/js/external-forms/form/Field.tsx +++ b/frontend/src/js/external-forms/form/Field.tsx @@ -33,12 +33,7 @@ import FormConceptGroup from "../form-concept-group/FormConceptGroup"; import type { FormConceptGroupT } from "../form-concept-group/formConceptGroupState"; import FormQueryDropzone from "../form-query-dropzone/FormQueryDropzone"; import FormTabNavigation from "../form-tab-navigation/FormTabNavigation"; -import { - getFieldKey, - getInitialValue, - isFormField, - isOptionalField, -} from "../helper"; +import { getFieldKey, getInitialValue, isFormField } from "../helper"; import { getErrorForField } from "../validators"; import type { DynamicFormValues } from "./Form"; @@ -165,35 +160,28 @@ const NestedFields = styled("div")` border-radius: ${({ theme }) => theme.borderRadius}; `; -interface PropsT { +const setValueConfig = { + shouldValidate: true, + shouldDirty: true, + shouldTouch: true, +}; + +const Field = ({ + field, + ...commonProps +}: { formType: string; h1Index?: number; field: GeneralField; locale: Language; availableDatasets: SelectOptionT[]; - optional?: boolean; register: UseFormRegister; setValue: UseFormSetValue; control: Control; -} - -const setValueConfig = { - shouldValidate: true, - shouldDirty: true, - shouldTouch: true, -}; - -const Field = ({ field, ...commonProps }: PropsT) => { +}) => { const datasetId = useDatasetId(); - const { - formType, - h1Index, - optional, - locale, - availableDatasets, - setValue, - control, - } = commonProps; + const { formType, h1Index, locale, availableDatasets, setValue, control } = + commonProps; const { t } = useTranslation(); const defaultValue = @@ -238,7 +226,6 @@ const Field = ({ field, ...commonProps }: PropsT) => { value={fieldProps.value as string} onChange={(value) => setValue(field.name, value, setValueConfig)} tooltip={field.tooltip ? field.tooltip[locale] : undefined} - optional={optional} /> )} @@ -263,7 +250,6 @@ const Field = ({ field, ...commonProps }: PropsT) => { setValue(field.name, value, setValueConfig); }} tooltip={field.tooltip ? field.tooltip[locale] : undefined} - optional={optional} /> )} @@ -292,7 +278,6 @@ const Field = ({ field, ...commonProps }: PropsT) => { max: field.max, }} tooltip={field.tooltip ? field.tooltip[locale] : undefined} - optional={optional} /> )} @@ -310,7 +295,6 @@ const Field = ({ field, ...commonProps }: PropsT) => { inline={true} label={field.label[locale]} tooltip={field.tooltip ? field.tooltip[locale] : undefined} - optional={optional} value={fieldProps.value as DateStringMinMax} onChange={(value) => setValue(field.name, value, setValueConfig) @@ -332,7 +316,6 @@ const Field = ({ field, ...commonProps }: PropsT) => { label={field.label[locale] || ""} dropzoneText={field.dropzoneLabel[locale] || ""} tooltip={field.tooltip ? field.tooltip[locale] : undefined} - optional={optional} value={fieldProps.value as DragItemQuery} onChange={(value) => setValue(field.name, value, setValueConfig)} /> @@ -374,7 +357,6 @@ const Field = ({ field, ...commonProps }: PropsT) => { label={field.label[locale]} options={options} tooltip={field.tooltip ? field.tooltip[locale] : undefined} - optional={optional} value={fieldProps.value as SelectOptionT | null} onChange={(value) => setValue(field.name, value, setValueConfig)} /> @@ -400,7 +382,6 @@ const Field = ({ field, ...commonProps }: PropsT) => { label={field.label[locale]} options={availableDatasets} tooltip={field.tooltip ? field.tooltip[locale] : undefined} - optional={optional} value={fieldProps.value as SelectOptionT | null} onChange={(value) => setValue(field.name, value, setValueConfig) @@ -428,16 +409,8 @@ const Field = ({ field, ...commonProps }: PropsT) => { > {field.fields.map((f, i) => { const key = getFieldKey(formType, f, i); - const nestedFieldOptional = isOptionalField(f); - return ( - - ); + return ; })} @@ -472,16 +445,8 @@ const Field = ({ field, ...commonProps }: PropsT) => { {tabToShow.fields.map((f, i) => { const key = getFieldKey(formType, f, i); - const nestedFieldOptional = isOptionalField(f); - return ( - - ); + return ; })} ) : ( @@ -524,7 +489,6 @@ const Field = ({ field, ...commonProps }: PropsT) => { blocklistedSelects={field.blocklistedSelects} allowlistedSelects={field.allowlistedSelects} defaults={field.defaults} - optional={optional} isValidConcept={(item) => !nodeIsInvalid( item, diff --git a/frontend/src/js/external-forms/form/Form.tsx b/frontend/src/js/external-forms/form/Form.tsx index 28be70f607..05d8a16c30 100644 --- a/frontend/src/js/external-forms/form/Form.tsx +++ b/frontend/src/js/external-forms/form/Form.tsx @@ -6,7 +6,7 @@ import type { SelectOptionT } from "../../api/types"; import { useActiveLang } from "../../localization/useActiveLang"; import FormHeader from "../FormHeader"; import type { Form as FormType } from "../config-types"; -import { getFieldKey, getH1Index, isOptionalField } from "../helper"; +import { getFieldKey, getH1Index } from "../helper"; import Field from "./Field"; @@ -44,7 +44,6 @@ const Form = memo(({ config, datasetOptions, methods }: Props) => { )} {config.fields.map((field, i) => { const key = getFieldKey(config.type, field, i); - const optional = isOptionalField(field); const h1Index = getH1Index(config.fields, field); return ( @@ -58,7 +57,6 @@ const Form = memo(({ config, datasetOptions, methods }: Props) => { setValue={methods.setValue} availableDatasets={datasetOptions} locale={activeLang} - optional={optional} /> ); })} diff --git a/frontend/src/js/external-forms/helper.ts b/frontend/src/js/external-forms/helper.ts index 0efdfc901a..c1ff843e84 100644 --- a/frontend/src/js/external-forms/helper.ts +++ b/frontend/src/js/external-forms/helper.ts @@ -42,15 +42,6 @@ export const getH1Index = (fields: GeneralField[], field: GeneralField) => { return h1Fields.indexOf(field); }; -export const isOptionalField = (field: GeneralField) => { - return ( - isFormField(field) && - (!("validations" in field) || - ("validations" in field && - (!field.validations || !field.validations.includes("NOT_EMPTY")))) - ); -}; - export const isFormField = (field: GeneralField): field is FormField => { return !nonFormFieldTypes.has(field.type); }; diff --git a/frontend/src/js/ui-components/InputDateRange.tsx b/frontend/src/js/ui-components/InputDateRange.tsx index 33d2bdb747..9327482ad1 100644 --- a/frontend/src/js/ui-components/InputDateRange.tsx +++ b/frontend/src/js/ui-components/InputDateRange.tsx @@ -20,7 +20,6 @@ import InfoTooltip from "../tooltip/InfoTooltip"; import InputDate from "./InputDate/InputDate"; import Label from "./Label"; import Labeled from "./Labeled"; -import Optional from "./Optional"; const Root = styled("div")<{ center?: boolean }>` text-align: ${({ center }) => (center ? "center" : "left")}; @@ -88,7 +87,6 @@ interface PropsT { center?: boolean; autoFocus?: boolean; tooltip?: string; - optional?: boolean; value: DateStringMinMax; onChange: (value: DateStringMinMax) => void; } @@ -115,7 +113,6 @@ const InputDateRange: FC = ({ labelSuffix, value, onChange, - optional, tooltip, }) => { const { t } = useTranslation(); @@ -179,7 +176,6 @@ const InputDateRange: FC = ({ {exists(indexPrefix) && # {indexPrefix}} - {optional && } {label} = ({ {labelSuffix && labelSuffix} ); - }, [t, label, labelSuffix, large, optional, tooltip, indexPrefix]); + }, [t, label, labelSuffix, large, tooltip, indexPrefix]); return ( diff --git a/frontend/src/js/ui-components/InputPlain/InputPlain.tsx b/frontend/src/js/ui-components/InputPlain/InputPlain.tsx index 2fb4df3375..7bedb969b8 100644 --- a/frontend/src/js/ui-components/InputPlain/InputPlain.tsx +++ b/frontend/src/js/ui-components/InputPlain/InputPlain.tsx @@ -17,7 +17,6 @@ const SxBaseInput = styled(BaseInput)<{ fullWidth?: boolean }>` interface Props { label: string; indexPrefix?: number; - optional?: boolean; inputType?: string; money?: boolean; className?: string; @@ -44,7 +43,6 @@ const InputPlain = forwardRef( large, indexPrefix, tooltip, - optional, inputType = "text", money, placeholder, @@ -65,7 +63,6 @@ const InputPlain = forwardRef( largeLabel={large} indexPrefix={indexPrefix} tooltip={tooltip} - optional={optional} > void; sortOptions?: (a: SelectOptionT, b: SelectOptionT, query: string) => number; }) => { @@ -347,7 +345,6 @@ const InputSelect = ({ } indexPrefix={indexPrefix} className={className} - optional={optional} > {Select} diff --git a/frontend/src/js/ui-components/InputTextarea/InputTextarea.tsx b/frontend/src/js/ui-components/InputTextarea/InputTextarea.tsx index 80980be2c6..ba97a43f0c 100644 --- a/frontend/src/js/ui-components/InputTextarea/InputTextarea.tsx +++ b/frontend/src/js/ui-components/InputTextarea/InputTextarea.tsx @@ -35,7 +35,6 @@ interface OtherProps { fullWidth?: boolean; indexPrefix?: number; tooltip?: string; - optional?: boolean; onChange: (value: string | null) => void; } @@ -49,16 +48,7 @@ export const InputTextarea = forwardRef< InputTextareaProps & OtherProps >( ( - { - label, - className, - fullWidth, - indexPrefix, - tooltip, - optional, - onChange, - ...props - }, + { label, className, fullWidth, indexPrefix, tooltip, onChange, ...props }, ref, ) => { const { t } = useTranslation(); @@ -70,7 +60,6 @@ export const InputTextarea = forwardRef< className={className} fullWidth tooltip={tooltip} - optional={optional} >