diff --git a/src/components/events/partials/ModalTabsAndPages/EventDetailsSchedulingTab.tsx b/src/components/events/partials/ModalTabsAndPages/EventDetailsSchedulingTab.tsx index bfba0c5ef0..eed002275e 100644 --- a/src/components/events/partials/ModalTabsAndPages/EventDetailsSchedulingTab.tsx +++ b/src/components/events/partials/ModalTabsAndPages/EventDetailsSchedulingTab.tsx @@ -192,20 +192,24 @@ const EventDetailsSchedulingTab = ({ values.scheduleEndHour, values.scheduleEndMinute, ); - dispatch(checkConflicts({ eventId, startDate, endDate, deviceId: values.captureAgent })).then( - r => { - if (r) { + const notifyNotUpdated = () => { + dispatch(addNotification({ + type: "error", + key: "EVENTS_NOT_UPDATED", + duration: -1, + context: NOTIFICATION_CONTEXT, + })); + }; + + dispatch(checkConflicts({ eventId, startDate, endDate, deviceId: values.captureAgent })).unwrap() + .then(({ hasSchedulingConflicts }) => { + if (!hasSchedulingConflicts) { dispatch(saveSchedulingInfo({ eventId, values, startDate, endDate })).then(); } else { - dispatch(addNotification({ - type: "error", - key: "EVENTS_NOT_UPDATED", - duration: -1, - context: NOTIFICATION_CONTEXT, - })); + notifyNotUpdated(); } - }, - ); + }) + .catch(notifyNotUpdated); }; // initial values of the formik form diff --git a/src/components/events/partials/wizards/scheduling/SchedulingTime.tsx b/src/components/events/partials/wizards/scheduling/SchedulingTime.tsx index 4e5812400d..0181171b66 100644 --- a/src/components/events/partials/wizards/scheduling/SchedulingTime.tsx +++ b/src/components/events/partials/wizards/scheduling/SchedulingTime.tsx @@ -47,7 +47,6 @@ const SchedulingTime = ({ handleChange={element => { if (element) { callbackHour(element.value); - // TODO: Allow for ChangeMultiple for NewSourcePage } }} placeholder={t(hourPlaceholder)} diff --git a/src/components/shared/Field.tsx b/src/components/shared/Field.tsx index 30c50c1710..d82a4ecb56 100644 --- a/src/components/shared/Field.tsx +++ b/src/components/shared/Field.tsx @@ -1,13 +1,25 @@ -import { FastField as FormikFastField } from "formik"; +import { FastField as FormikFastField, FastFieldConfig } from "formik"; /** - * Wrapper for the Formik Fields + * Wrapper for the Formik Fields. + * + * `FormikFastField` itself is typed as `React.FC`, so + * `React.ComponentProps` would just resolve to `any`. + * We derive from Formik's own `FastFieldConfig` instead (rather than hand- + * copying its shape), so this stays correct if Formik's config props change. + * `component`/`as` are re-typed more loosely than Formik declares them: this + * app passes app-specific extra props (e.g. `metadataField`) into custom + * components via sibling attributes on ``, which isn't something + * Formik's own types can verify either - it types its `Field` the same way. */ -// TODO: Add strong typing -// The line below is currently just a fancy way of saying "any" -// Find a way to properly type this wrapper -type FieldProps = React.ComponentProps; -export const Field = (props: FieldProps) => { +type FieldProps = Omit, "component" | "as"> & { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + component?: string | React.ComponentType, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + as?: string | React.ComponentType, +} & Record; + +export const Field = (props: FieldProps) => { return ( 1) { arrToSort.sort((a, b) => { - const aPriority = a.path === pathname ? 0 : 1; - const bPriority = b.path === pathname ? 0 : 1; - - return aPriority - bPriority; + const aIndex = a.path === pathname ? 0 : 1; + const bIndex = b.path === pathname ? 0 : 1; + return aIndex - bIndex; }); } } diff --git a/src/components/shared/wizard/RenderField.tsx b/src/components/shared/wizard/RenderField.tsx index eb6800cdae..97c89ebeec 100644 --- a/src/components/shared/wizard/RenderField.tsx +++ b/src/components/shared/wizard/RenderField.tsx @@ -32,9 +32,11 @@ const RenderField = ({ }) => { const { t } = useTranslation(); - // TODO: Figure out how to type a ref that could have multiple types - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const editableRef = useRef(null); + // Only one of the following is ever mounted at once, chosen by metadataField.type below. + const inputRef = useRef(null); + const textareaRef = useRef(null); + const datePickerRef = useRef(null); + const selectRef = useRef, boolean, GroupBase>>>(null); const [focused, setFocused] = useState(false); const onFocus = () => setFocused(true); const onBlur = () => setFocused(false); @@ -42,18 +44,10 @@ const RenderField = ({ return (
{ - if (editableRef.current) { - // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access - if (editableRef.current.focus) { - // eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access - editableRef.current.focus(); - } - // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access - if (editableRef.current.setFocus) { - // eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access - editableRef.current.setFocus(); // For DatePicker - } - } + inputRef.current?.focus(); + textareaRef.current?.focus(); + selectRef.current?.focus(); + datePickerRef.current?.setFocus(); }} onFocus={onFocus} onBlur={onBlur} @@ -64,7 +58,7 @@ const RenderField = ({ field={field} form={form} isFirstField={isFirstField} - ref={editableRef} + ref={datePickerRef} /> )} {metadataField.type === "text" && @@ -78,7 +72,7 @@ const RenderField = ({ isFirstField={isFirstField} focused={focused} setFocused={setFocused} - ref={editableRef} + ref={selectRef} /> )} {metadataField.type === "ordered_text" && ( @@ -90,7 +84,7 @@ const RenderField = ({ isFirstField={isFirstField} focused={focused} setFocused={setFocused} - ref={editableRef} + ref={selectRef} /> )} {metadataField.type === "text" && @@ -100,14 +94,14 @@ const RenderField = ({ )} {metadataField.type === "text_long" && ( )} {metadataField.type === "date" && ( @@ -115,14 +109,14 @@ const RenderField = ({ field={field} form={form} isFirstField={isFirstField} - ref={editableRef} + ref={datePickerRef} /> )} {metadataField.type === "boolean" && ( )}
@@ -148,7 +142,7 @@ const EditableBooleanValue = ({ }: { field: FieldProps["field"] isFirstField?: boolean, - ref: React.RefObject + ref: React.RefObject }) => { return ( + ref: React.RefObject }) => { return ( // For some reason onclick events are bubbling up from the datepicker which we do not want. @@ -254,7 +248,7 @@ const EditableSingleValueTextArea = ({ }: { field: FieldProps["field"] isFirstField?: boolean, - ref: React.RefObject + ref: React.RefObject }) => { return ( // Maybe replace TextareaAutosize with css "field-sizing: content" once all @@ -304,7 +298,7 @@ const EditableSingleValueTime = ({ field: FieldProps["field"] form: FieldProps["form"] isFirstField?: boolean, - ref: React.RefObject + ref: React.RefObject }) => { return ( // For some reason onclick events are bubbling up from the datepicker which we do not want. diff --git a/src/slices/eventDetailsSlice.ts b/src/slices/eventDetailsSlice.ts index 8ae42dffde..dcdc833ef6 100644 --- a/src/slices/eventDetailsSlice.ts +++ b/src/slices/eventDetailsSlice.ts @@ -1190,8 +1190,6 @@ export const saveSchedulingInfo = createAppAsyncThunk("eventDetails/saveScheduli return source; }); -// TODO: This does not return a boolean anymore. Fix this in usage, make users -// get their info from the state export const checkConflicts = createAppAsyncThunk("eventDetails/checkConflicts", async (params: { eventId: Event["id"], startDate: Date, @@ -1530,7 +1528,6 @@ export const fetchWorkflowErrorDetails = createAppAsyncThunk("eventDetails/fetch return data.data; }); -// TODO: Fix this after the modernization of statisticsThunks happened export const fetchEventStatistics = createAppAsyncThunk("eventDetails/fetchEventStatistics", async (eventId: Event["id"], { getState }) => { // get prior statistics const state = getState(); @@ -1545,7 +1542,6 @@ export const fetchEventStatistics = createAppAsyncThunk("eventDetails/fetchEvent ); }); -// TODO: Fix this after the modernization of statisticsThunks happened export const fetchEventStatisticsValueUpdate = createAppAsyncThunk("eventDetails/fetchEventStatisticsValueUpdate", async (params: { id: Event["id"], providerId: string, @@ -2367,9 +2363,9 @@ const eventDetailsSlice = createSlice({ }) .addCase(fetchWorkflowDetails.rejected, (state, action) => { state.statusWorkflowDetails = "failed"; - // This is the empty workflow data from the original reducer - // TODO: Figure out why it is so vastly different from our initial state - // and maybe fix our initial state if this is actually correct + // Falls back to the same placeholder as our initial state (workflowId/description + // only, not the full workflow-details shape); consumers already narrow on + // `"status" in workflow` before reading details-only fields, so this degrades safely. const emptyWorkflowData = { workflowId: "", description: "",