From a4cf14b11b16cf2cc86c883d6ba6735815eac2e6 Mon Sep 17 00:00:00 2001 From: Travis Jenkins Date: Mon, 16 Sep 2024 13:26:31 -0400 Subject: [PATCH 01/66] Turning everything back on --- src/app/guards/EntityExistenceGuard.tsx | 5 ++-- .../editor/Bindings/Backfill/index.tsx | 8 +++--- src/components/shared/ChipList/Wrapper.tsx | 10 +++++++- src/components/shared/Entity/Actions/Save.tsx | 25 +++++++++++-------- .../shared/Entity/CatalogEditor.tsx | 15 ++++++----- src/components/shared/Entity/Edit/index.tsx | 3 ++- .../shared/Entity/prompts/PreSave/index.tsx | 4 +-- .../SelectMaterialization/Selector.tsx | 2 ++ 8 files changed, 47 insertions(+), 25 deletions(-) diff --git a/src/app/guards/EntityExistenceGuard.tsx b/src/app/guards/EntityExistenceGuard.tsx index fc80f8be5..48bb7506f 100644 --- a/src/app/guards/EntityExistenceGuard.tsx +++ b/src/app/guards/EntityExistenceGuard.tsx @@ -5,6 +5,7 @@ import useGlobalSearchParams, { } from 'hooks/searchParams/useGlobalSearchParams'; import { useLiveSpecsExtWithSpec } from 'hooks/useLiveSpecsExt'; import EntityNotFound from 'pages/error/EntityNotFound'; +import { useFormStateStore_setLiveSpec } from 'stores/FormState/hooks'; import { BaseComponentProps } from 'types'; function EntityExistenceGuard({ children }: BaseComponentProps) { @@ -13,7 +14,7 @@ function EntityExistenceGuard({ children }: BaseComponentProps) { const entityType = useEntityType(); // TODO (data flow reset) - // const setLiveSpec = useFormStateStore_setLiveSpec(); + const setLiveSpec = useFormStateStore_setLiveSpec(); const { liveSpecs, isValidating: checkingEntityExistence } = useLiveSpecsExtWithSpec(liveSpecId, entityType); @@ -24,7 +25,7 @@ function EntityExistenceGuard({ children }: BaseComponentProps) { return ; } else { // TODO (data flow reset) - // setLiveSpec(liveSpecs[0].spec); + setLiveSpec(liveSpecs[0].spec); // eslint-disable-next-line react/jsx-no-useless-fragment return <>{children}; diff --git a/src/components/editor/Bindings/Backfill/index.tsx b/src/components/editor/Bindings/Backfill/index.tsx index ad0cc6176..e24b56ad3 100644 --- a/src/components/editor/Bindings/Backfill/index.tsx +++ b/src/components/editor/Bindings/Backfill/index.tsx @@ -1,6 +1,7 @@ import { Box, Stack, Typography } from '@mui/material'; import BooleanToggleButton from 'components/shared/buttons/BooleanToggleButton'; import { BooleanString } from 'components/shared/buttons/types'; +import { useEntityWorkflow } from 'context/Workflow'; import { useCallback, useMemo } from 'react'; import { useIntl } from 'react-intl'; import { @@ -20,6 +21,7 @@ import { import { FormStatus } from 'stores/FormState/types'; import { useEditorStore_queryResponse_draftSpecs } from '../../Store/hooks'; import BackfillCount from './BackfillCount'; +import BackfillDataFlowOption from './BackfillDataFlowOption'; import BackfillNotSupportedAlert from './BackfillNotSupportedAlert'; import { BackfillProps } from './types'; import useUpdateBackfillCounter, { @@ -31,7 +33,7 @@ function Backfill({ description, bindingIndex = -1 }: BackfillProps) { const { updateBackfillCounter } = useUpdateBackfillCounter(); // TODO (data flow reset) - // const workflow = useEntityWorkflow(); + const workflow = useEntityWorkflow(); // Binding Store const currentCollection = useBinding_currentCollection(); @@ -200,9 +202,9 @@ function Backfill({ description, bindingIndex = -1 }: BackfillProps) { {/*TODO (data flow reset)*/} - {/* {bindingIndex === -1 && workflow === 'capture_edit' ? ( + {bindingIndex === -1 && workflow === 'capture_edit' ? ( - ) : null}*/} + ) : null} ); } diff --git a/src/components/shared/ChipList/Wrapper.tsx b/src/components/shared/ChipList/Wrapper.tsx index 79743e2af..186437fdd 100644 --- a/src/components/shared/ChipList/Wrapper.tsx +++ b/src/components/shared/ChipList/Wrapper.tsx @@ -80,7 +80,15 @@ function ChipWrapper({ size="small" variant="outlined" disabled={disabled} - onClick={onClick} + onClick={ + onClick + ? (event) => { + event.stopPropagation(); + event.preventDefault(); + onClick(); + } + : undefined + } sx={chipSX} /> ); diff --git a/src/components/shared/Entity/Actions/Save.tsx b/src/components/shared/Entity/Actions/Save.tsx index 923f5c80d..da6993992 100644 --- a/src/components/shared/Entity/Actions/Save.tsx +++ b/src/components/shared/Entity/Actions/Save.tsx @@ -5,8 +5,13 @@ import { } from 'components/editor/Store/hooks'; import { buttonSx } from 'components/shared/Entity/Header'; import { useIntl } from 'react-intl'; +import { useBinding_backfilledBindings_count } from 'stores/Binding/hooks'; +import { useBindingStore } from 'stores/Binding/Store'; -import { useFormStateStore_isActive } from 'stores/FormState/hooks'; +import { + useFormStateStore_isActive, + useFormStateStore_setShowPreSavePrompt, +} from 'stores/FormState/hooks'; import { EntityCreateSaveButtonProps } from './types'; import useSave from './useSave'; @@ -27,9 +32,9 @@ function EntityCreateSave({ const draftId = useEditorStore_id(); // TODO (data flow reset) - // const setShowPreSavePrompt = useFormStateStore_setShowPreSavePrompt(); - // const backfillDataflow = useBindingStore((state) => state.backfillDataFlow); - // const needsBackfilled = useBinding_backfilledBindings_count(); + const setShowPreSavePrompt = useFormStateStore_setShowPreSavePrompt(); + const backfillDataflow = useBindingStore((state) => state.backfillDataFlow); + const needsBackfilled = useBinding_backfilledBindings_count(); return ( - diff --git a/src/components/shared/Entity/prompts/PreSave/Content.tsx b/src/components/shared/Entity/prompts/PreSave/Content.tsx index e8d0607b1..9651e7a96 100644 --- a/src/components/shared/Entity/prompts/PreSave/Content.tsx +++ b/src/components/shared/Entity/prompts/PreSave/Content.tsx @@ -1,19 +1,26 @@ -import { DialogContent, Step, Stepper } from '@mui/material'; +import { DialogContent, LinearProgress, Step, Stepper } from '@mui/material'; import { useMemo } from 'react'; -import usePreSavePromptSteps from '../steps/preSave/usePreSavePromptSteps'; +import { usePreSavePromptStore } from '../store/usePreSavePromptStore'; function Content() { - const { activeStep, steps } = usePreSavePromptSteps(); + const [activeStep, steps] = usePreSavePromptStore((state) => [ + state.activeStep, + state.steps, + ]); const renderedSteps = useMemo( () => - steps.map((StepComponent, index) => { - return ( - - - - ); - }), + steps ? ( + steps.map(({ StepComponent }, index) => { + return ( + + + + ); + }) + ) : ( + + ), [steps] ); diff --git a/src/components/shared/Entity/prompts/PreSave/Title.tsx b/src/components/shared/Entity/prompts/PreSave/Title.tsx index b81be8dff..cbb441fbd 100644 --- a/src/components/shared/Entity/prompts/PreSave/Title.tsx +++ b/src/components/shared/Entity/prompts/PreSave/Title.tsx @@ -1,19 +1,19 @@ import { DialogTitle, IconButton, useTheme } from '@mui/material'; import { Xmark } from 'iconoir-react'; import { useIntl } from 'react-intl'; -import { useFormStateStore_setShowPreSavePrompt } from 'stores/FormState/hooks'; -import usePreSavePromptSteps from '../steps/preSave/usePreSavePromptSteps'; +import { usePreSavePromptStore } from '../store/usePreSavePromptStore'; function Title() { const intl = useIntl(); const theme = useTheme(); - const { activeStep, setActiveStep } = usePreSavePromptSteps(); - const setShowPreSavePrompt = useFormStateStore_setShowPreSavePrompt(); + const [activeStep, setActiveStep, setShow] = usePreSavePromptStore( + (state) => [state.activeStep, state.setActiveStep, state.setShow] + ); const closeDialog = () => { setActiveStep(0); - setShowPreSavePrompt(false); + setShow(false); }; return ( diff --git a/src/components/shared/Entity/prompts/PreSave/index.tsx b/src/components/shared/Entity/prompts/PreSave/index.tsx index 8109d8551..a29f71fbb 100644 --- a/src/components/shared/Entity/prompts/PreSave/index.tsx +++ b/src/components/shared/Entity/prompts/PreSave/index.tsx @@ -1,21 +1,17 @@ import { Dialog } from '@mui/material'; -import { useFormStateStore_showPreSavePrompt } from 'stores/FormState/hooks'; -import usePreSavePromptSteps from '../steps/preSave/usePreSavePromptSteps'; +import { usePreSavePromptStore } from '../store/usePreSavePromptStore'; import Actions from './Actions'; import Content from './Content'; import Title from './Title'; function PreSavePrompt() { - const { activeStep } = usePreSavePromptSteps(); - - const showPreSavePrompt = useFormStateStore_showPreSavePrompt(); + const [activeStep, show] = usePreSavePromptStore((state) => [ + state.activeStep, + state.show, + ]); return ( - + <Content /> <Actions /> diff --git a/src/components/shared/Entity/prompts/steps/dataFlowReset/SelectMaterialization/Materializations.tsx b/src/components/shared/Entity/prompts/steps/dataFlowReset/SelectMaterialization/Materializations.tsx index a77b4d8fa..b727e49b3 100644 --- a/src/components/shared/Entity/prompts/steps/dataFlowReset/SelectMaterialization/Materializations.tsx +++ b/src/components/shared/Entity/prompts/steps/dataFlowReset/SelectMaterialization/Materializations.tsx @@ -1,11 +1,11 @@ import { Box, LinearProgress, Typography } from '@mui/material'; import Error from 'components/shared/Error'; -import { useConfirmationModalContext } from 'context/Confirmation'; import { useLiveSpecsExt_related } from 'hooks/useLiveSpecsExt'; import { useEffect, useMemo } from 'react'; import { useIntl } from 'react-intl'; import { useBindingStore } from 'stores/Binding/Store'; import { hasLength } from 'utils/misc-utils'; +import { usePreSavePromptStore } from '../../../store/usePreSavePromptStore'; import Selector from './Selector'; import { BindingReviewProps } from './types'; @@ -14,7 +14,7 @@ function Materializations({ selected }: BindingReviewProps) { const { related, error, isValidating } = useLiveSpecsExt_related(selected); - const confirmationModal = useConfirmationModalContext(); + const updateStep = usePreSavePromptStore((state) => state.updateStep); const foundData = useMemo(() => hasLength(related), [related]); @@ -23,13 +23,12 @@ function Materializations({ selected }: BindingReviewProps) { ]); useEffect(() => { - // TODO (data flow reset) - // This needs to get worked into the steps somehow.... the steps need to be able to say - // they are "allowed to continue" - confirmationModal?.setContinueAllowed( - Boolean((!isValidating && !foundData) || backfillDataFlowTarget) - ); - }, [foundData, isValidating, confirmationModal, backfillDataFlowTarget]); + updateStep({ + valid: Boolean( + (!isValidating && !foundData) || backfillDataFlowTarget + ), + }); + }, [backfillDataFlowTarget, foundData, isValidating, updateStep]); return ( <Box> diff --git a/src/components/shared/Entity/prompts/steps/dataFlowReset/shared.ts b/src/components/shared/Entity/prompts/steps/dataFlowReset/shared.ts index 7fdf66b9e..c77238358 100644 --- a/src/components/shared/Entity/prompts/steps/dataFlowReset/shared.ts +++ b/src/components/shared/Entity/prompts/steps/dataFlowReset/shared.ts @@ -1,14 +1,33 @@ -import { ReactJSXElement } from '@emotion/react/types/jsx-namespace'; +import { defaultStepState } from '../../store/shared'; +import { PromptStep } from '../../types'; import DisableCapture from './DisableCapture'; import EnableCapture from './EnableCapture'; import SelectMaterialization from './SelectMaterialization'; import MarkMaterialization from './UpdateMaterialization'; import WaitForCaptureStop from './WaitForCaptureStop'; -export const DataFlowResetSteps: (() => ReactJSXElement)[] = [ - SelectMaterialization, - DisableCapture, - WaitForCaptureStop, - MarkMaterialization, - EnableCapture, +export const DataFlowResetSteps: PromptStep[] = [ + { + StepComponent: SelectMaterialization, + state: { + ...defaultStepState, + valid: false, + }, + }, + { + StepComponent: DisableCapture, + state: defaultStepState, + }, + { + StepComponent: WaitForCaptureStop, + state: defaultStepState, + }, + { + StepComponent: MarkMaterialization, + state: defaultStepState, + }, + { + StepComponent: EnableCapture, + state: defaultStepState, + }, ]; diff --git a/src/components/shared/Entity/prompts/steps/preSave/usePreSavePromptSteps.tsx b/src/components/shared/Entity/prompts/steps/preSave/usePreSavePromptSteps.tsx index 82d6ceb57..92fb6c9ef 100644 --- a/src/components/shared/Entity/prompts/steps/preSave/usePreSavePromptSteps.tsx +++ b/src/components/shared/Entity/prompts/steps/preSave/usePreSavePromptSteps.tsx @@ -1,56 +1,24 @@ -import { ReactJSXElement } from '@emotion/react/types/jsx-namespace'; -import { useMemo } from 'react'; -import { createGlobalState } from 'react-use'; +import { useEffect } from 'react'; import { useBinding_backfilledBindings_count } from 'stores/Binding/hooks'; import { useBindingStore } from 'stores/Binding/Store'; -import { DataFlowResetSteps } from '../dataFlowReset/shared'; -import ChangeReviewStep from './ChangeReview'; -import Done from './Done'; -import Publish from './Publish'; - -// TODO (data flow reset) this stuff should go into a store -// also we probably need to keep if a step is done within the step itself -// that way a user could go back and view the outcome of a state while -// other states are running. - -// step data sketch -// label - string or node -// content - react node -// done - boolean -// errors - array of issues (cannot continue until gone) -// status? - maybe... need to know if it is running or not? -// action? - maybe... storing off the functions/hooks/whatevs that will run on this step -export const useGlobalValue = createGlobalState<number>(0); +import { usePreSavePromptStore } from '../../store/usePreSavePromptStore'; function usePreSavePromptSteps() { - const [activeStep, setActiveStep] = useGlobalValue(); + const [resetState, initializeSteps] = usePreSavePromptStore((state) => [ + state.resetState, + state.initializeSteps, + ]); const backfillDataflow = useBindingStore((state) => state.backfillDataFlow); const needsBackfilled = useBinding_backfilledBindings_count(); - const steps = useMemo(() => { - const response: (() => ReactJSXElement)[] = [ChangeReviewStep]; - - if (backfillDataflow && needsBackfilled) { - response.push(...DataFlowResetSteps); - } - - response.push(Publish, Done); - - return response; - }, [backfillDataflow, needsBackfilled]); + useEffect(() => { + initializeSteps(Boolean(backfillDataflow && needsBackfilled)); - return { - steps, - activeStep, - setActiveStep, - handleBack: () => { - setActiveStep((prevActiveStep) => prevActiveStep - 1); - }, - handleNext: () => { - setActiveStep((prevActiveStep) => prevActiveStep + 1); - }, - }; + return () => { + resetState(); + }; + }, [backfillDataflow, initializeSteps, needsBackfilled, resetState]); } export default usePreSavePromptSteps; diff --git a/src/components/shared/Entity/prompts/store/Hydrator.tsx b/src/components/shared/Entity/prompts/store/Hydrator.tsx new file mode 100644 index 000000000..9e57831c4 --- /dev/null +++ b/src/components/shared/Entity/prompts/store/Hydrator.tsx @@ -0,0 +1,28 @@ +import { BaseComponentProps } from 'types'; +import { useEffect } from 'react'; +import { useBinding_backfilledBindings_count } from 'stores/Binding/hooks'; +import { useBindingStore } from 'stores/Binding/Store'; +import { usePreSavePromptStore } from './usePreSavePromptStore'; + +function PromptsHydrator({ children }: BaseComponentProps) { + const [resetState, initializeSteps] = usePreSavePromptStore((state) => [ + state.resetState, + state.initializeSteps, + ]); + + const backfillDataflow = useBindingStore((state) => state.backfillDataFlow); + const needsBackfilled = useBinding_backfilledBindings_count(); + + useEffect(() => { + initializeSteps(Boolean(backfillDataflow && needsBackfilled)); + + return () => { + resetState(); + }; + }, [backfillDataflow, initializeSteps, needsBackfilled, resetState]); + + // eslint-disable-next-line react/jsx-no-useless-fragment + return <>{children}</>; +} + +export default PromptsHydrator; diff --git a/src/components/shared/Entity/prompts/store/shared.ts b/src/components/shared/Entity/prompts/store/shared.ts new file mode 100644 index 000000000..500d9ff35 --- /dev/null +++ b/src/components/shared/Entity/prompts/store/shared.ts @@ -0,0 +1,8 @@ +import { PromptStepState } from '../types'; + +export const defaultStepState: PromptStepState = { + done: false, + errors: null, + running: false, + valid: true, +}; diff --git a/src/components/shared/Entity/prompts/store/usePreSavePromptStore.ts b/src/components/shared/Entity/prompts/store/usePreSavePromptStore.ts new file mode 100644 index 000000000..5f2762c93 --- /dev/null +++ b/src/components/shared/Entity/prompts/store/usePreSavePromptStore.ts @@ -0,0 +1,139 @@ +import produce from 'immer'; +import { create } from 'zustand'; +import { devtools } from 'zustand/middleware'; +import { devtoolsOptions } from 'utils/store-utils'; +import { PromptStep, PromptStepState } from '../types'; +import ChangeReview from '../steps/preSave/ChangeReview'; +import { DataFlowResetSteps } from '../steps/dataFlowReset/shared'; +import Publish from '../steps/preSave/Publish'; +import { defaultStepState } from './shared'; + +interface PreSavePromptStore { + steps: PromptStep[] | null; + initializeSteps: (backfillEnabled: boolean) => void; + updateStep: (settings: Partial<PromptStepState>, step?: number) => void; + + activeStep: number; + setActiveStep: (val: PreSavePromptStore['activeStep']) => void; + nextStep: () => void; + previousStep: () => void; + + show: boolean; + setShow: (data: PreSavePromptStore['show']) => void; + + resetState: () => void; +} + +const getInitialState = (): Pick< + PreSavePromptStore, + 'activeStep' | 'steps' | 'show' +> => ({ + activeStep: 0, + show: false, + steps: null, +}); + +const name = 'estuary.presave-prompt-store'; + +export const usePreSavePromptStore = create<PreSavePromptStore>()( + devtools((set) => { + return { + ...getInitialState(), + resetState: () => set(getInitialState(), false, 'state reset'), + + initializeSteps: (backfillEnabled) => + set( + produce((state: PreSavePromptStore) => { + const newSteps: PromptStep[] = [ + { + StepComponent: ChangeReview, + state: { + ...defaultStepState, + valid: true, + }, + }, + ]; + + if (backfillEnabled) { + newSteps.push(...DataFlowResetSteps); + } + + newSteps.push({ + StepComponent: Publish, + state: defaultStepState, + }); + state.steps = newSteps; + }), + false, + 'initializeSteps' + ), + + updateStep: (settings, step) => + set( + produce((state: PreSavePromptStore) => { + if (!state.steps) { + return; + } + + const stepToUpdate = step ?? state.activeStep; + + state.steps[stepToUpdate].state = { + ...state.steps[stepToUpdate].state, + ...settings, + }; + }), + false, + 'setActiveStep' + ), + + setActiveStep: (value) => + set( + produce((state: PreSavePromptStore) => { + state.activeStep = value; + }), + false, + 'setActiveStep' + ), + + nextStep: () => + set( + produce((state: PreSavePromptStore) => { + if (!state.steps) { + return; + } + + if (state.steps[state.activeStep].state.valid) { + state.steps[state.activeStep].state.done = true; + state.activeStep = state.activeStep + 1; + } + }), + false, + 'nextStep' + ), + + previousStep: () => + set( + produce((state: PreSavePromptStore) => { + state.activeStep = state.activeStep - 1; + }), + false, + 'nextStep' + ), + + setShow: (val) => + set( + produce((state: PreSavePromptStore) => { + state.show = val; + }), + false, + 'setShow' + ), + }; + }, devtoolsOptions(name)) +); + +export const usePreSavePromptStore_activeStep_state = () => { + return usePreSavePromptStore( + (state) => state.steps?.[state.activeStep].state + ); +}; diff --git a/src/components/shared/Entity/prompts/types.ts b/src/components/shared/Entity/prompts/types.ts new file mode 100644 index 000000000..5aa88a91c --- /dev/null +++ b/src/components/shared/Entity/prompts/types.ts @@ -0,0 +1,20 @@ +import { EmotionJSX } from '@emotion/react/types/jsx-namespace'; + +export interface PromptStepState { + done: boolean; + + // Both server and client side error + errors: any[] | null; + running: boolean; + + // Store whatever you want in here + settings?: any; + + // Controls if the user can continue on from this step + valid: boolean; +} + +export interface PromptStep { + StepComponent: () => EmotionJSX.Element; + state: PromptStepState; +} diff --git a/src/stores/FormState/Store.ts b/src/stores/FormState/Store.ts index c48474fde..d04027253 100644 --- a/src/stores/FormState/Store.ts +++ b/src/stores/FormState/Store.ts @@ -83,12 +83,7 @@ const getInitialStateData = ( messagePrefix: MessagePrefixes ): Pick< EntityFormState, - | 'formState' - | 'isIdle' - | 'isActive' - | 'messagePrefix' - | 'showPreSavePrompt' - | 'liveSpec' + 'formState' | 'isIdle' | 'isActive' | 'messagePrefix' | 'liveSpec' > => ({ formState: initialFormState, @@ -96,7 +91,6 @@ const getInitialStateData = ( isActive: false, liveSpec: null, - showPreSavePrompt: false, messagePrefix, }); @@ -178,16 +172,6 @@ const getInitialState = ( ); }, - setShowPreSavePrompt: (newVal) => { - set( - produce((state: EntityFormState) => { - state.showPreSavePrompt = newVal; - }), - false, - 'Show Change Review Updated' - ); - }, - setLiveSpec: (newVal) => { set( produce((state: EntityFormState) => { diff --git a/src/stores/FormState/hooks.ts b/src/stores/FormState/hooks.ts index 40cd08391..24df6919f 100644 --- a/src/stores/FormState/hooks.ts +++ b/src/stores/FormState/hooks.ts @@ -139,24 +139,6 @@ export const useFormStateStore_messagePrefix = () => { ); }; -export const useFormStateStore_showPreSavePrompt = () => { - const workflow = useEntityWorkflow(); - - return useZustandStore< - EntityFormState, - EntityFormState['showPreSavePrompt'] - >(storeName(workflow), (state) => state.showPreSavePrompt); -}; - -export const useFormStateStore_setShowPreSavePrompt = () => { - const workflow = useEntityWorkflow(); - - return useZustandStore< - EntityFormState, - EntityFormState['setShowPreSavePrompt'] - >(storeName(workflow), (state) => state.setShowPreSavePrompt); -}; - export const useFormStateStore_liveSpec = () => { const workflow = useEntityWorkflow(); diff --git a/src/stores/FormState/types.ts b/src/stores/FormState/types.ts index 04600783e..f3871c88f 100644 --- a/src/stores/FormState/types.ts +++ b/src/stores/FormState/types.ts @@ -50,9 +50,6 @@ export interface EntityFormState { isIdle: boolean; isActive: boolean; - showPreSavePrompt: boolean; - setShowPreSavePrompt: (data: EntityFormState['showPreSavePrompt']) => void; - liveSpec: Schema | null; setLiveSpec: (data: EntityFormState['liveSpec']) => void; From 89736ca5b933d9bbc30b39df9a6747e82fccccae Mon Sep 17 00:00:00 2001 From: Travis Jenkins <travis@estuary.dev> Date: Mon, 16 Sep 2024 18:30:34 -0400 Subject: [PATCH 03/66] Getting actions buttons wired up Calling intl for translations --- .../shared/Entity/prompts/PreSave/Actions.tsx | 18 ++++++++++-------- .../shared/Entity/prompts/store/shared.ts | 1 + .../prompts/store/usePreSavePromptStore.ts | 9 ++++++--- src/components/shared/Entity/prompts/types.ts | 5 +++++ 4 files changed, 22 insertions(+), 11 deletions(-) diff --git a/src/components/shared/Entity/prompts/PreSave/Actions.tsx b/src/components/shared/Entity/prompts/PreSave/Actions.tsx index f8604b8a1..bab0ea7ff 100644 --- a/src/components/shared/Entity/prompts/PreSave/Actions.tsx +++ b/src/components/shared/Entity/prompts/PreSave/Actions.tsx @@ -1,23 +1,25 @@ import { Button, DialogActions, Stack } from '@mui/material'; -import { usePreSavePromptStore } from '../store/usePreSavePromptStore'; +import { useIntl } from 'react-intl'; +import { + usePreSavePromptStore, + usePreSavePromptStore_activeStepValid, +} from '../store/usePreSavePromptStore'; function Actions() { + const intl = useIntl(); + const [nextStep, previousStep] = usePreSavePromptStore((state) => [ state.nextStep, state.previousStep, ]); - const continueEnabled = usePreSavePromptStore((state) => { - return state.steps?.[state.activeStep]?.state.valid; - }); - - console.log('continueEnabled', continueEnabled); + const continueEnabled = usePreSavePromptStore_activeStepValid(); return ( <DialogActions> <Stack direction="row" spacing={2}> <Button onClick={previousStep} variant="text"> - Back + {intl.formatMessage({ id: 'cta.back' })} </Button> <Button @@ -25,7 +27,7 @@ function Actions() { variant="outlined" disabled={!continueEnabled} > - Continue + {intl.formatMessage({ id: 'cta.continue' })} </Button> </Stack> </DialogActions> diff --git a/src/components/shared/Entity/prompts/store/shared.ts b/src/components/shared/Entity/prompts/store/shared.ts index 500d9ff35..8c0841605 100644 --- a/src/components/shared/Entity/prompts/store/shared.ts +++ b/src/components/shared/Entity/prompts/store/shared.ts @@ -4,5 +4,6 @@ export const defaultStepState: PromptStepState = { done: false, errors: null, running: false, + started: false, valid: true, }; diff --git a/src/components/shared/Entity/prompts/store/usePreSavePromptStore.ts b/src/components/shared/Entity/prompts/store/usePreSavePromptStore.ts index 5f2762c93..732ab5fee 100644 --- a/src/components/shared/Entity/prompts/store/usePreSavePromptStore.ts +++ b/src/components/shared/Entity/prompts/store/usePreSavePromptStore.ts @@ -2,6 +2,7 @@ import produce from 'immer'; import { create } from 'zustand'; import { devtools } from 'zustand/middleware'; import { devtoolsOptions } from 'utils/store-utils'; +import { useShallow } from 'zustand/react/shallow'; import { PromptStep, PromptStepState } from '../types'; import ChangeReview from '../steps/preSave/ChangeReview'; import { DataFlowResetSteps } from '../steps/dataFlowReset/shared'; @@ -10,8 +11,8 @@ import { defaultStepState } from './shared'; interface PreSavePromptStore { steps: PromptStep[] | null; - initializeSteps: (backfillEnabled: boolean) => void; updateStep: (settings: Partial<PromptStepState>, step?: number) => void; + initializeSteps: (backfillEnabled: boolean) => void; activeStep: number; setActiveStep: (val: PreSavePromptStore['activeStep']) => void; @@ -132,8 +133,10 @@ export const usePreSavePromptStore = create<PreSavePromptStore>()( }, devtoolsOptions(name)) ); -export const usePreSavePromptStore_activeStep_state = () => { +export const usePreSavePromptStore_activeStepValid = () => { return usePreSavePromptStore( - (state) => state.steps?.[state.activeStep].state + useShallow((state) => { + return state.steps?.[state.activeStep]?.state.valid; + }) ); }; diff --git a/src/components/shared/Entity/prompts/types.ts b/src/components/shared/Entity/prompts/types.ts index 5aa88a91c..139846950 100644 --- a/src/components/shared/Entity/prompts/types.ts +++ b/src/components/shared/Entity/prompts/types.ts @@ -5,11 +5,16 @@ export interface PromptStepState { // Both server and client side error errors: any[] | null; + + // Shows if it is actively running running: boolean; // Store whatever you want in here settings?: any; + // Stores if we have ever tried _once_ + started: boolean; + // Controls if the user can continue on from this step valid: boolean; } From 9a03b2185c2750feb902649eda22986fec5f31d4 Mon Sep 17 00:00:00 2001 From: Travis Jenkins <travis@estuary.dev> Date: Mon, 16 Sep 2024 18:48:01 -0400 Subject: [PATCH 04/66] Moving more stuff into content Moving label into store to reduce duplication Adding a running handler into parent --- .../shared/Entity/CatalogEditor.tsx | 4 +- .../shared/Entity/prompts/PreSave/Content.tsx | 36 ------------ .../Entity/prompts/PreSave/Content/index.tsx | 55 +++++++++++++++++++ .../dataFlowReset/DisableCapture/index.tsx | 27 +++------ .../dataFlowReset/EnableCapture/index.tsx | 12 +--- .../SelectMaterialization/BindingReview.tsx | 6 +- .../SelectMaterialization/index.tsx | 13 +---- .../UpdateMaterialization/index.tsx | 14 +---- .../WaitForCaptureStop/index.tsx | 15 +---- .../prompts/steps/dataFlowReset/shared.ts | 5 ++ .../steps/preSave/ChangeReview/index.tsx | 13 +---- .../prompts/steps/preSave/Done/index.tsx | 17 ------ .../prompts/steps/preSave/Publish/index.tsx | 18 +++--- .../steps/preSave/usePreSavePromptSteps.tsx | 24 -------- .../prompts/store/usePreSavePromptStore.ts | 3 + src/components/shared/Entity/prompts/types.ts | 1 + src/lang/en-US/Workflows.ts | 25 ++++++--- 17 files changed, 110 insertions(+), 178 deletions(-) delete mode 100644 src/components/shared/Entity/prompts/PreSave/Content.tsx create mode 100644 src/components/shared/Entity/prompts/PreSave/Content/index.tsx delete mode 100644 src/components/shared/Entity/prompts/steps/preSave/Done/index.tsx delete mode 100644 src/components/shared/Entity/prompts/steps/preSave/usePreSavePromptSteps.tsx diff --git a/src/components/shared/Entity/CatalogEditor.tsx b/src/components/shared/Entity/CatalogEditor.tsx index 2493733ab..1ad8d4123 100644 --- a/src/components/shared/Entity/CatalogEditor.tsx +++ b/src/components/shared/Entity/CatalogEditor.tsx @@ -51,11 +51,11 @@ function CatalogEditor({ messageId }: Props) { short severity="warning" title={intl.formatMessage({ - id: 'dataflowReset.editor.warning.title', + id: 'dataFlowReset.editor.warning.title', })} > {intl.formatMessage({ - id: 'dataflowReset.editor.warning.message', + id: 'dataFlowReset.editor.warning.message', })} </AlertBox> ) : null} diff --git a/src/components/shared/Entity/prompts/PreSave/Content.tsx b/src/components/shared/Entity/prompts/PreSave/Content.tsx deleted file mode 100644 index 9651e7a96..000000000 --- a/src/components/shared/Entity/prompts/PreSave/Content.tsx +++ /dev/null @@ -1,36 +0,0 @@ -import { DialogContent, LinearProgress, Step, Stepper } from '@mui/material'; -import { useMemo } from 'react'; -import { usePreSavePromptStore } from '../store/usePreSavePromptStore'; - -function Content() { - const [activeStep, steps] = usePreSavePromptStore((state) => [ - state.activeStep, - state.steps, - ]); - - const renderedSteps = useMemo( - () => - steps ? ( - steps.map(({ StepComponent }, index) => { - return ( - <Step key={`PreSave-step-${index}`}> - <StepComponent /> - </Step> - ); - }) - ) : ( - <LinearProgress /> - ), - [steps] - ); - - return ( - <DialogContent> - <Stepper orientation="vertical" activeStep={activeStep}> - {renderedSteps} - </Stepper> - </DialogContent> - ); -} - -export default Content; diff --git a/src/components/shared/Entity/prompts/PreSave/Content/index.tsx b/src/components/shared/Entity/prompts/PreSave/Content/index.tsx new file mode 100644 index 000000000..24c0ac320 --- /dev/null +++ b/src/components/shared/Entity/prompts/PreSave/Content/index.tsx @@ -0,0 +1,55 @@ +import { + DialogContent, + LinearProgress, + Step, + StepContent, + StepLabel, + Stepper, +} from '@mui/material'; +import ErrorBoundryWrapper from 'components/shared/ErrorBoundryWrapper'; +import { useMemo } from 'react'; +import { useIntl } from 'react-intl'; +import { usePreSavePromptStore } from '../../store/usePreSavePromptStore'; + +function Content() { + const intl = useIntl(); + const [activeStep, steps] = usePreSavePromptStore((state) => [ + state.activeStep, + state.steps, + ]); + + const renderedSteps = useMemo( + () => + steps ? ( + steps.map((step, index) => { + const { StepComponent, stepLabelMessageId, state } = step; + return ( + <Step key={`PreSave-step-${index}`}> + <StepLabel> + {intl.formatMessage({ id: stepLabelMessageId })} + </StepLabel> + <StepContent> + <ErrorBoundryWrapper> + {state.running ? <LinearProgress /> : null} + <StepComponent /> + </ErrorBoundryWrapper> + </StepContent> + </Step> + ); + }) + ) : ( + <LinearProgress /> + ), + [intl, steps] + ); + + return ( + <DialogContent> + <Stepper orientation="vertical" activeStep={activeStep}> + {renderedSteps} + </Stepper> + </DialogContent> + ); +} + +export default Content; diff --git a/src/components/shared/Entity/prompts/steps/dataFlowReset/DisableCapture/index.tsx b/src/components/shared/Entity/prompts/steps/dataFlowReset/DisableCapture/index.tsx index b85016ce2..3f505b105 100644 --- a/src/components/shared/Entity/prompts/steps/dataFlowReset/DisableCapture/index.tsx +++ b/src/components/shared/Entity/prompts/steps/dataFlowReset/DisableCapture/index.tsx @@ -1,25 +1,16 @@ -import { StepContent, StepLabel } from '@mui/material'; -import ErrorBoundryWrapper from 'components/shared/ErrorBoundryWrapper'; import Logs from 'components/logs'; function DisableCapture() { return ( - <> - <StepLabel>Disable capture</StepLabel> - <StepContent> - <ErrorBoundryWrapper> - <Logs - token={null} - height={350} - loadingLineSeverity="info" - spinnerMessages={{ - stoppedKey: 'dataflowReset.logs.spinner.stopped', - runningKey: 'dataflowReset.logs.spinner.running', - }} - /> - </ErrorBoundryWrapper> - </StepContent> - </> + <Logs + token={null} + height={350} + loadingLineSeverity="info" + spinnerMessages={{ + stoppedKey: 'preSavePrompt.logs.spinner.stopped', + runningKey: 'preSavePrompt.logs.spinner.running', + }} + /> ); } diff --git a/src/components/shared/Entity/prompts/steps/dataFlowReset/EnableCapture/index.tsx b/src/components/shared/Entity/prompts/steps/dataFlowReset/EnableCapture/index.tsx index 6678a5797..7cea411f6 100644 --- a/src/components/shared/Entity/prompts/steps/dataFlowReset/EnableCapture/index.tsx +++ b/src/components/shared/Entity/prompts/steps/dataFlowReset/EnableCapture/index.tsx @@ -1,15 +1,5 @@ -import { StepContent, StepLabel } from '@mui/material'; -import ErrorBoundryWrapper from 'components/shared/ErrorBoundryWrapper'; - function EnableCapture() { - return ( - <> - <StepLabel>Enable capture</StepLabel> - <StepContent> - <ErrorBoundryWrapper>Logs</ErrorBoundryWrapper> - </StepContent> - </> - ); + return <>Logs</>; } export default EnableCapture; diff --git a/src/components/shared/Entity/prompts/steps/dataFlowReset/SelectMaterialization/BindingReview.tsx b/src/components/shared/Entity/prompts/steps/dataFlowReset/SelectMaterialization/BindingReview.tsx index 7275a9aa3..36825dda8 100644 --- a/src/components/shared/Entity/prompts/steps/dataFlowReset/SelectMaterialization/BindingReview.tsx +++ b/src/components/shared/Entity/prompts/steps/dataFlowReset/SelectMaterialization/BindingReview.tsx @@ -23,15 +23,15 @@ function BindingReview() { short severity="warning" title={intl.formatMessage({ - id: 'dataflowReset.warning.title', + id: 'dataFlowReset.warning.title', })} > - {intl.formatMessage({ id: 'dataflowReset.warning.message' })} + {intl.formatMessage({ id: 'dataFlowReset.warning.message' })} </AlertBox> <Typography> {intl.formatMessage( - { id: 'dataflowReset.step1.message' }, + { id: 'dataFlowReset.step1.message' }, { entityCount: collectionsBeingBackfilled.length, } diff --git a/src/components/shared/Entity/prompts/steps/dataFlowReset/SelectMaterialization/index.tsx b/src/components/shared/Entity/prompts/steps/dataFlowReset/SelectMaterialization/index.tsx index 8e69e0e99..d07ecaec4 100644 --- a/src/components/shared/Entity/prompts/steps/dataFlowReset/SelectMaterialization/index.tsx +++ b/src/components/shared/Entity/prompts/steps/dataFlowReset/SelectMaterialization/index.tsx @@ -1,18 +1,7 @@ -import { StepContent, StepLabel } from '@mui/material'; -import ErrorBoundryWrapper from 'components/shared/ErrorBoundryWrapper'; import BindingReview from './BindingReview'; function SelectMaterialization() { - return ( - <> - <StepLabel>Select materialization for data flow reset</StepLabel> - <StepContent> - <ErrorBoundryWrapper> - <BindingReview /> - </ErrorBoundryWrapper> - </StepContent> - </> - ); + return <BindingReview />; } export default SelectMaterialization; diff --git a/src/components/shared/Entity/prompts/steps/dataFlowReset/UpdateMaterialization/index.tsx b/src/components/shared/Entity/prompts/steps/dataFlowReset/UpdateMaterialization/index.tsx index efe8f9739..81122112b 100644 --- a/src/components/shared/Entity/prompts/steps/dataFlowReset/UpdateMaterialization/index.tsx +++ b/src/components/shared/Entity/prompts/steps/dataFlowReset/UpdateMaterialization/index.tsx @@ -1,17 +1,5 @@ -import { StepContent, StepLabel } from '@mui/material'; -import ErrorBoundryWrapper from 'components/shared/ErrorBoundryWrapper'; - function MarkMaterialization() { - return ( - <> - <StepLabel>Mark materialization notBefore</StepLabel> - <StepContent> - <ErrorBoundryWrapper> - describe what we're doing - </ErrorBoundryWrapper> - </StepContent> - </> - ); + return <>describe what we're doing</>; } export default MarkMaterialization; diff --git a/src/components/shared/Entity/prompts/steps/dataFlowReset/WaitForCaptureStop/index.tsx b/src/components/shared/Entity/prompts/steps/dataFlowReset/WaitForCaptureStop/index.tsx index fcb43169e..0783e89f6 100644 --- a/src/components/shared/Entity/prompts/steps/dataFlowReset/WaitForCaptureStop/index.tsx +++ b/src/components/shared/Entity/prompts/steps/dataFlowReset/WaitForCaptureStop/index.tsx @@ -1,18 +1,5 @@ -import { LinearProgress, StepContent, StepLabel } from '@mui/material'; -import ErrorBoundryWrapper from 'components/shared/ErrorBoundryWrapper'; - function WaitForCaptureStop() { - return ( - <> - <StepLabel>Wait for capture data to stop</StepLabel> - <StepContent> - <ErrorBoundryWrapper> - <LinearProgress /> - No logs... but show something - </ErrorBoundryWrapper> - </StepContent> - </> - ); + return <>No logs... but show something</>; } export default WaitForCaptureStop; diff --git a/src/components/shared/Entity/prompts/steps/dataFlowReset/shared.ts b/src/components/shared/Entity/prompts/steps/dataFlowReset/shared.ts index c77238358..efb7119d3 100644 --- a/src/components/shared/Entity/prompts/steps/dataFlowReset/shared.ts +++ b/src/components/shared/Entity/prompts/steps/dataFlowReset/shared.ts @@ -9,6 +9,7 @@ import WaitForCaptureStop from './WaitForCaptureStop'; export const DataFlowResetSteps: PromptStep[] = [ { StepComponent: SelectMaterialization, + stepLabelMessageId: 'dataFlowReset.selectMaterialization.title', state: { ...defaultStepState, valid: false, @@ -16,18 +17,22 @@ export const DataFlowResetSteps: PromptStep[] = [ }, { StepComponent: DisableCapture, + stepLabelMessageId: 'dataFlowReset.disableCapture.title', state: defaultStepState, }, { StepComponent: WaitForCaptureStop, + stepLabelMessageId: 'dataFlowReset.waitForCapture.title', state: defaultStepState, }, { StepComponent: MarkMaterialization, + stepLabelMessageId: 'dataFlowReset.markMaterialization.title', state: defaultStepState, }, { StepComponent: EnableCapture, + stepLabelMessageId: 'dataFlowReset.enableCapture.title', state: defaultStepState, }, ]; diff --git a/src/components/shared/Entity/prompts/steps/preSave/ChangeReview/index.tsx b/src/components/shared/Entity/prompts/steps/preSave/ChangeReview/index.tsx index d9d6e9346..177768eab 100644 --- a/src/components/shared/Entity/prompts/steps/preSave/ChangeReview/index.tsx +++ b/src/components/shared/Entity/prompts/steps/preSave/ChangeReview/index.tsx @@ -1,18 +1,7 @@ -import { StepContent, StepLabel } from '@mui/material'; -import ErrorBoundryWrapper from 'components/shared/ErrorBoundryWrapper'; import DiffViewer from './DiffViewer'; function ChangeReview() { - return ( - <> - <StepLabel>How the spec is changing</StepLabel> - <StepContent> - <ErrorBoundryWrapper> - <DiffViewer /> - </ErrorBoundryWrapper> - </StepContent> - </> - ); + return <DiffViewer />; } export default ChangeReview; diff --git a/src/components/shared/Entity/prompts/steps/preSave/Done/index.tsx b/src/components/shared/Entity/prompts/steps/preSave/Done/index.tsx deleted file mode 100644 index a458f6a87..000000000 --- a/src/components/shared/Entity/prompts/steps/preSave/Done/index.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import { StepContent, StepLabel } from '@mui/material'; -import ErrorBoundryWrapper from 'components/shared/ErrorBoundryWrapper'; - -function Done() { - return ( - <> - <StepLabel>Done</StepLabel> - <StepContent> - <ErrorBoundryWrapper> - Congrats you are done! - </ErrorBoundryWrapper> - </StepContent> - </> - ); -} - -export default Done; diff --git a/src/components/shared/Entity/prompts/steps/preSave/Publish/index.tsx b/src/components/shared/Entity/prompts/steps/preSave/Publish/index.tsx index 12b11fbfa..88c465889 100644 --- a/src/components/shared/Entity/prompts/steps/preSave/Publish/index.tsx +++ b/src/components/shared/Entity/prompts/steps/preSave/Publish/index.tsx @@ -1,14 +1,16 @@ -import { StepContent, StepLabel } from '@mui/material'; -import ErrorBoundryWrapper from 'components/shared/ErrorBoundryWrapper'; +import Logs from 'components/logs'; function Publish() { return ( - <> - <StepLabel>Publishing</StepLabel> - <StepContent> - <ErrorBoundryWrapper>Logs</ErrorBoundryWrapper> - </StepContent> - </> + <Logs + token={null} + height={350} + loadingLineSeverity="info" + spinnerMessages={{ + stoppedKey: 'preSavePrompt.logs.spinner.stopped', + runningKey: 'preSavePrompt.logs.spinner.running', + }} + /> ); } diff --git a/src/components/shared/Entity/prompts/steps/preSave/usePreSavePromptSteps.tsx b/src/components/shared/Entity/prompts/steps/preSave/usePreSavePromptSteps.tsx deleted file mode 100644 index 92fb6c9ef..000000000 --- a/src/components/shared/Entity/prompts/steps/preSave/usePreSavePromptSteps.tsx +++ /dev/null @@ -1,24 +0,0 @@ -import { useEffect } from 'react'; -import { useBinding_backfilledBindings_count } from 'stores/Binding/hooks'; -import { useBindingStore } from 'stores/Binding/Store'; -import { usePreSavePromptStore } from '../../store/usePreSavePromptStore'; - -function usePreSavePromptSteps() { - const [resetState, initializeSteps] = usePreSavePromptStore((state) => [ - state.resetState, - state.initializeSteps, - ]); - - const backfillDataflow = useBindingStore((state) => state.backfillDataFlow); - const needsBackfilled = useBinding_backfilledBindings_count(); - - useEffect(() => { - initializeSteps(Boolean(backfillDataflow && needsBackfilled)); - - return () => { - resetState(); - }; - }, [backfillDataflow, initializeSteps, needsBackfilled, resetState]); -} - -export default usePreSavePromptSteps; diff --git a/src/components/shared/Entity/prompts/store/usePreSavePromptStore.ts b/src/components/shared/Entity/prompts/store/usePreSavePromptStore.ts index 732ab5fee..90d97516d 100644 --- a/src/components/shared/Entity/prompts/store/usePreSavePromptStore.ts +++ b/src/components/shared/Entity/prompts/store/usePreSavePromptStore.ts @@ -48,6 +48,8 @@ export const usePreSavePromptStore = create<PreSavePromptStore>()( const newSteps: PromptStep[] = [ { StepComponent: ChangeReview, + stepLabelMessageId: + 'preSavePrompt.changeReview.title', state: { ...defaultStepState, valid: true, @@ -61,6 +63,7 @@ export const usePreSavePromptStore = create<PreSavePromptStore>()( newSteps.push({ StepComponent: Publish, + stepLabelMessageId: 'preSavePrompt.publish.title', state: defaultStepState, }); state.steps = newSteps; diff --git a/src/components/shared/Entity/prompts/types.ts b/src/components/shared/Entity/prompts/types.ts index 139846950..edf5a039d 100644 --- a/src/components/shared/Entity/prompts/types.ts +++ b/src/components/shared/Entity/prompts/types.ts @@ -21,5 +21,6 @@ export interface PromptStepState { export interface PromptStep { StepComponent: () => EmotionJSX.Element; + stepLabelMessageId: string; state: PromptStepState; } diff --git a/src/lang/en-US/Workflows.ts b/src/lang/en-US/Workflows.ts index 80f42885f..94e74a7bf 100644 --- a/src/lang/en-US/Workflows.ts +++ b/src/lang/en-US/Workflows.ts @@ -127,20 +127,29 @@ export const Workflows: Record<string, string> = { 'workflows.disable.message': `Control whether your {entityType} is disabled. This setting takes effect when your changes are published.`, 'workflows.disable.update.error': `Failed to update {entityType}. Please check your network connection and try again.`, + // PreSave prompts + 'preSavePrompt.changeReview.title': `How the spec is changing`, + 'preSavePrompt.publish.title': `Save and publish`, + 'preSavePrompt.logs.spinner.stopped': `done`, + 'preSavePrompt.logs.spinner.running': `loading...`, + + 'dataFlowReset.selectMaterialization.title': `Select materialization for data flow reset`, + 'dataFlowReset.disableCapture.title': `Disable capture`, + 'dataFlowReset.waitForCapture.title': `Wait for capture to fully stop`, + 'dataFlowReset.markMaterialization.title': `Update Materialization`, + 'dataFlowReset.enableCapture.title': `Enable capture`, + // Dataflow reset 'workflows.collectionSelector.dataFlowBackfill.header': `Choose to backfill just your capture or the entire ${CommonMessages['terms.dataFlow']}.`, 'workflows.collectionSelector.dataFlowBackfill.option': `Backfill Capture`, 'workflows.collectionSelector.dataFlowBackfill.message': `Backfill capture and reset corresponding tables in materialization.`, - 'dataflowReset.warning.title': `This cannot be undone or stopped`, - 'dataflowReset.warning.message': `Once this process is started you must stay on this page. Do not click away or reload the page. If you have any issues please contact support immediately as we may need to assist you in recovery.`, - 'dataflowReset.step1.message': `The {entityCount} collections to be backfilled`, - - 'dataflowReset.editor.warning.title': `Editing disabled`, - 'dataflowReset.editor.warning.message': `While backfilling the ${CommonMessages['terms.dataFlow']} you cannot manually edit your spec.`, + 'dataFlowReset.warning.title': `This cannot be undone or stopped`, + 'dataFlowReset.warning.message': `Once this process is started you must stay on this page. Do not click away or reload the page. If you have any issues please contact support immediately as we may need to assist you in recovery.`, + 'dataFlowReset.step1.message': `The {entityCount} collections to be backfilled`, - 'dataflowReset.logs.spinner.stopped': `done`, - 'dataflowReset.logs.spinner.running': `loading...`, + 'dataFlowReset.editor.warning.title': `Editing disabled`, + 'dataFlowReset.editor.warning.message': `While backfilling the ${CommonMessages['terms.dataFlow']} you cannot manually edit your spec.`, // Field Selection 'fieldSelection.header': `Field Selection`, From 46775cab03ca386692c073e43225f62524245fcd Mon Sep 17 00:00:00 2001 From: Travis Jenkins <travis@estuary.dev> Date: Mon, 16 Sep 2024 18:50:26 -0400 Subject: [PATCH 05/66] checking first step --- src/components/shared/Entity/prompts/PreSave/Actions.tsx | 8 +++++++- .../shared/Entity/prompts/store/usePreSavePromptStore.ts | 8 ++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/src/components/shared/Entity/prompts/PreSave/Actions.tsx b/src/components/shared/Entity/prompts/PreSave/Actions.tsx index bab0ea7ff..c3a77a81a 100644 --- a/src/components/shared/Entity/prompts/PreSave/Actions.tsx +++ b/src/components/shared/Entity/prompts/PreSave/Actions.tsx @@ -3,6 +3,7 @@ import { useIntl } from 'react-intl'; import { usePreSavePromptStore, usePreSavePromptStore_activeStepValid, + usePreSavePromptStore_onFirstStep, } from '../store/usePreSavePromptStore'; function Actions() { @@ -14,11 +15,16 @@ function Actions() { ]); const continueEnabled = usePreSavePromptStore_activeStepValid(); + const onFirstStep = usePreSavePromptStore_onFirstStep(); return ( <DialogActions> <Stack direction="row" spacing={2}> - <Button onClick={previousStep} variant="text"> + <Button + onClick={previousStep} + variant="text" + disabled={onFirstStep} + > {intl.formatMessage({ id: 'cta.back' })} </Button> diff --git a/src/components/shared/Entity/prompts/store/usePreSavePromptStore.ts b/src/components/shared/Entity/prompts/store/usePreSavePromptStore.ts index 90d97516d..7740a8018 100644 --- a/src/components/shared/Entity/prompts/store/usePreSavePromptStore.ts +++ b/src/components/shared/Entity/prompts/store/usePreSavePromptStore.ts @@ -143,3 +143,11 @@ export const usePreSavePromptStore_activeStepValid = () => { }) ); }; + +export const usePreSavePromptStore_onFirstStep = () => { + return usePreSavePromptStore( + useShallow((state) => { + return state.activeStep === 0; + }) + ); +}; From c0491d3694b3ff49c0a63a63d63761b9714a620c Mon Sep 17 00:00:00 2001 From: Travis Jenkins <travis@estuary.dev> Date: Tue, 17 Sep 2024 17:08:50 -0400 Subject: [PATCH 06/66] Updating typing a lot Switching to uses as single status property Getting capture disable wired up --- .../shared/Entity/prompts/PreSave/Actions.tsx | 6 +- .../Entity/prompts/PreSave/Content/index.tsx | 8 +- .../dataFlowReset/DisableCapture/index.tsx | 103 ++++++++++++++++-- .../prompts/steps/dataFlowReset/shared.ts | 2 + .../shared/Entity/prompts/store/shared.ts | 6 +- .../prompts/store/usePreSavePromptStore.ts | 8 +- src/components/shared/Entity/prompts/types.ts | 19 ++-- .../RowActions/AccessGrants/Progress.tsx | 2 +- .../RowActions/AccessGrants/RevokeGrant.tsx | 2 +- .../AccessLinks/DisableDirective.tsx | 2 +- .../RowActions/AccessLinks/Progress.tsx | 2 +- .../tables/RowActions/Shared/Progress.tsx | 21 +--- .../tables/RowActions/Shared/UpdateEntity.tsx | 6 +- .../tables/RowActions/Shared/types.ts | 22 ++++ src/services/supabase.ts | 6 +- 15 files changed, 154 insertions(+), 61 deletions(-) create mode 100644 src/components/tables/RowActions/Shared/types.ts diff --git a/src/components/shared/Entity/prompts/PreSave/Actions.tsx b/src/components/shared/Entity/prompts/PreSave/Actions.tsx index c3a77a81a..fc00c6a1e 100644 --- a/src/components/shared/Entity/prompts/PreSave/Actions.tsx +++ b/src/components/shared/Entity/prompts/PreSave/Actions.tsx @@ -2,7 +2,7 @@ import { Button, DialogActions, Stack } from '@mui/material'; import { useIntl } from 'react-intl'; import { usePreSavePromptStore, - usePreSavePromptStore_activeStepValid, + usePreSavePromptStore_activeStep, usePreSavePromptStore_onFirstStep, } from '../store/usePreSavePromptStore'; @@ -14,7 +14,7 @@ function Actions() { state.previousStep, ]); - const continueEnabled = usePreSavePromptStore_activeStepValid(); + const activeStep = usePreSavePromptStore_activeStep(); const onFirstStep = usePreSavePromptStore_onFirstStep(); return ( @@ -31,7 +31,7 @@ function Actions() { <Button onClick={nextStep} variant="outlined" - disabled={!continueEnabled} + disabled={!Boolean(activeStep?.valid)} > {intl.formatMessage({ id: 'cta.continue' })} </Button> diff --git a/src/components/shared/Entity/prompts/PreSave/Content/index.tsx b/src/components/shared/Entity/prompts/PreSave/Content/index.tsx index 24c0ac320..c85a8a384 100644 --- a/src/components/shared/Entity/prompts/PreSave/Content/index.tsx +++ b/src/components/shared/Entity/prompts/PreSave/Content/index.tsx @@ -7,6 +7,7 @@ import { Stepper, } from '@mui/material'; import ErrorBoundryWrapper from 'components/shared/ErrorBoundryWrapper'; +import { ProgressStates } from 'components/tables/RowActions/Shared/types'; import { useMemo } from 'react'; import { useIntl } from 'react-intl'; import { usePreSavePromptStore } from '../../store/usePreSavePromptStore'; @@ -30,8 +31,11 @@ function Content() { </StepLabel> <StepContent> <ErrorBoundryWrapper> - {state.running ? <LinearProgress /> : null} - <StepComponent /> + {state.progress === + ProgressStates.RUNNING ? ( + <LinearProgress /> + ) : null} + <StepComponent stepIndex={index} /> </ErrorBoundryWrapper> </StepContent> </Step> diff --git a/src/components/shared/Entity/prompts/steps/dataFlowReset/DisableCapture/index.tsx b/src/components/shared/Entity/prompts/steps/dataFlowReset/DisableCapture/index.tsx index 3f505b105..019b22934 100644 --- a/src/components/shared/Entity/prompts/steps/dataFlowReset/DisableCapture/index.tsx +++ b/src/components/shared/Entity/prompts/steps/dataFlowReset/DisableCapture/index.tsx @@ -1,16 +1,97 @@ -import Logs from 'components/logs'; +import { modifyDraftSpec } from 'api/draftSpecs'; +import { createPublication, getPublicationByIdQuery } from 'api/publications'; +import { + useEditorStore_id, + useEditorStore_queryResponse_draftSpecs, +} from 'components/editor/Store/hooks'; +import { ProgressStates } from 'components/tables/RowActions/Shared/types'; +import useJobStatusPoller from 'hooks/useJobStatusPoller'; +import { useMount } from 'react-use'; +import { generateDisabledSpec } from 'utils/entity-utils'; +import { usePreSavePromptStore } from '../../../store/usePreSavePromptStore'; +import { StepComponentProps } from '../../../types'; + +function DisableCapture({ stepIndex }: StepComponentProps) { + const draftId = useEditorStore_id(); + const draftSpecs = useEditorStore_queryResponse_draftSpecs(); + const { jobStatusPoller } = useJobStatusPoller(); + + const thisStep = usePreSavePromptStore((state) => state.steps?.[stepIndex]); + const [updateStep, nextStep] = usePreSavePromptStore((state) => [ + state.updateStep, + state.nextStep, + ]); + + useMount(() => { + if (thisStep?.state.progress === ProgressStates.IDLE) { + updateStep({ + progress: ProgressStates.RUNNING, + }); + + const foo = async () => { + // Update the Capture to be disabled + const updateResponse = await modifyDraftSpec( + generateDisabledSpec(draftSpecs[0].spec, false, false), + { + draft_id: draftId, + catalog_name: draftSpecs[0].catalog_name, + spec_type: 'capture', + } + ); + + if (updateResponse.error) { + console.log('uh oh'); + return; + } + + // Start publishing it + const publishResponse = await createPublication(draftId, false); + + if (publishResponse.error || !publishResponse.data) { + console.log('uh oh'); + return; + } + + updateStep({ + logsToken: publishResponse.data[0].logs_token, + }); + + jobStatusPoller( + getPublicationByIdQuery(publishResponse.data[0].id), + async () => { + updateStep({ + progress: ProgressStates.SUCCESS, + valid: true, + }); + + nextStep(); + }, + async (error: any) => { + updateStep({ + errors: error, + progress: ProgressStates.FAILED, + valid: false, + }); + // logRocketEvent(CustomEvents.REPUBLISH_PREFIX_FAILED); + } + ); + }; + + void foo(); + } + }); -function DisableCapture() { return ( - <Logs - token={null} - height={350} - loadingLineSeverity="info" - spinnerMessages={{ - stoppedKey: 'preSavePrompt.logs.spinner.stopped', - runningKey: 'preSavePrompt.logs.spinner.running', - }} - /> + <>explain what we're doing</> + // <Logs + // token={logsToken} + // height={350} + // loadingLineSeverity="info" + // spinnerMessages={{ + // stoppedKey: 'preSavePrompt.logs.spinner.stopped', + // runningKey: 'preSavePrompt.logs.spinner.running', + // }} + // /> ); } diff --git a/src/components/shared/Entity/prompts/steps/dataFlowReset/shared.ts b/src/components/shared/Entity/prompts/steps/dataFlowReset/shared.ts index efb7119d3..f2bd7cbec 100644 --- a/src/components/shared/Entity/prompts/steps/dataFlowReset/shared.ts +++ b/src/components/shared/Entity/prompts/steps/dataFlowReset/shared.ts @@ -6,6 +6,8 @@ import SelectMaterialization from './SelectMaterialization'; import MarkMaterialization from './UpdateMaterialization'; import WaitForCaptureStop from './WaitForCaptureStop'; +// !!!!!!!!!ORDER IS IMPORTANT!!!!!!!!!!!! +// We run through steps in order export const DataFlowResetSteps: PromptStep[] = [ { StepComponent: SelectMaterialization, diff --git a/src/components/shared/Entity/prompts/store/shared.ts b/src/components/shared/Entity/prompts/store/shared.ts index 8c0841605..4b0b668fb 100644 --- a/src/components/shared/Entity/prompts/store/shared.ts +++ b/src/components/shared/Entity/prompts/store/shared.ts @@ -1,9 +1,9 @@ +import { ProgressStates } from 'components/tables/RowActions/Shared/types'; import { PromptStepState } from '../types'; export const defaultStepState: PromptStepState = { - done: false, errors: null, - running: false, + progress: ProgressStates.IDLE, started: false, - valid: true, + valid: false, }; diff --git a/src/components/shared/Entity/prompts/store/usePreSavePromptStore.ts b/src/components/shared/Entity/prompts/store/usePreSavePromptStore.ts index 7740a8018..4fab54055 100644 --- a/src/components/shared/Entity/prompts/store/usePreSavePromptStore.ts +++ b/src/components/shared/Entity/prompts/store/usePreSavePromptStore.ts @@ -3,6 +3,7 @@ import { create } from 'zustand'; import { devtools } from 'zustand/middleware'; import { devtoolsOptions } from 'utils/store-utils'; import { useShallow } from 'zustand/react/shallow'; +import { ProgressStates } from 'components/tables/RowActions/Shared/types'; import { PromptStep, PromptStepState } from '../types'; import ChangeReview from '../steps/preSave/ChangeReview'; import { DataFlowResetSteps } from '../steps/dataFlowReset/shared'; @@ -107,7 +108,8 @@ export const usePreSavePromptStore = create<PreSavePromptStore>()( } if (state.steps[state.activeStep].state.valid) { - state.steps[state.activeStep].state.done = true; + state.steps[state.activeStep].state.progress = + ProgressStates.SUCCESS; state.activeStep = state.activeStep + 1; } }), @@ -136,10 +138,10 @@ export const usePreSavePromptStore = create<PreSavePromptStore>()( }, devtoolsOptions(name)) ); -export const usePreSavePromptStore_activeStepValid = () => { +export const usePreSavePromptStore_activeStep = () => { return usePreSavePromptStore( useShallow((state) => { - return state.steps?.[state.activeStep]?.state.valid; + return state.steps?.[state.activeStep]?.state; }) ); }; diff --git a/src/components/shared/Entity/prompts/types.ts b/src/components/shared/Entity/prompts/types.ts index edf5a039d..1577614d5 100644 --- a/src/components/shared/Entity/prompts/types.ts +++ b/src/components/shared/Entity/prompts/types.ts @@ -1,26 +1,29 @@ import { EmotionJSX } from '@emotion/react/types/jsx-namespace'; +import { ProgressStates } from 'components/tables/RowActions/Shared/types'; export interface PromptStepState { - done: boolean; - // Both server and client side error errors: any[] | null; - // Shows if it is actively running - running: boolean; - - // Store whatever you want in here - settings?: any; + // Stores what the step is currently doing + progress: ProgressStates; // Stores if we have ever tried _once_ started: boolean; // Controls if the user can continue on from this step valid: boolean; + + // Used to show logging + logsToken?: string; +} + +export interface StepComponentProps { + stepIndex: number; } export interface PromptStep { - StepComponent: () => EmotionJSX.Element; + StepComponent: (props: StepComponentProps) => EmotionJSX.Element; stepLabelMessageId: string; state: PromptStepState; } diff --git a/src/components/tables/RowActions/AccessGrants/Progress.tsx b/src/components/tables/RowActions/AccessGrants/Progress.tsx index 2769e4430..6e7415b0c 100644 --- a/src/components/tables/RowActions/AccessGrants/Progress.tsx +++ b/src/components/tables/RowActions/AccessGrants/Progress.tsx @@ -8,7 +8,7 @@ import { import Error from 'components/shared/Error'; import { CheckCircle, WarningCircle } from 'iconoir-react'; import { FormattedMessage } from 'react-intl'; -import { ProgressStates } from '../Shared/Progress'; +import { ProgressStates } from '../Shared/types'; interface Props { error: any | null; diff --git a/src/components/tables/RowActions/AccessGrants/RevokeGrant.tsx b/src/components/tables/RowActions/AccessGrants/RevokeGrant.tsx index d3d3988bf..c3a1a7426 100644 --- a/src/components/tables/RowActions/AccessGrants/RevokeGrant.tsx +++ b/src/components/tables/RowActions/AccessGrants/RevokeGrant.tsx @@ -1,6 +1,6 @@ import { deleteRoleGrant } from 'api/roleGrants'; import { deleteUserGrant } from 'api/userGrants'; -import { ProgressStates } from 'components/tables/RowActions/Shared/Progress'; +import { ProgressStates } from 'components/tables/RowActions/Shared/types'; import { useZustandStore } from 'context/Zustand/provider'; import { Dispatch, SetStateAction, useEffect, useState } from 'react'; import { useMount } from 'react-use'; diff --git a/src/components/tables/RowActions/AccessLinks/DisableDirective.tsx b/src/components/tables/RowActions/AccessLinks/DisableDirective.tsx index 00663a4f3..d72333c1e 100644 --- a/src/components/tables/RowActions/AccessLinks/DisableDirective.tsx +++ b/src/components/tables/RowActions/AccessLinks/DisableDirective.tsx @@ -1,6 +1,6 @@ import { disableDirective } from 'api/directives'; import Progress from 'components/tables/RowActions/AccessLinks/Progress'; -import { ProgressStates } from 'components/tables/RowActions/Shared/Progress'; +import { ProgressStates } from 'components/tables/RowActions/Shared/types'; import { useZustandStore } from 'context/Zustand/provider'; import { Dispatch, SetStateAction, useEffect, useState } from 'react'; import { SelectTableStoreNames } from 'stores/names'; diff --git a/src/components/tables/RowActions/AccessLinks/Progress.tsx b/src/components/tables/RowActions/AccessLinks/Progress.tsx index 4a6d908bb..919a76088 100644 --- a/src/components/tables/RowActions/AccessLinks/Progress.tsx +++ b/src/components/tables/RowActions/AccessLinks/Progress.tsx @@ -8,7 +8,7 @@ import { import Error from 'components/shared/Error'; import { CheckCircle, WarningCircle } from 'iconoir-react'; import { FormattedMessage } from 'react-intl'; -import { ProgressStates } from '../Shared/Progress'; +import { ProgressStates } from '../Shared/types'; interface Props { progress: ProgressStates; diff --git a/src/components/tables/RowActions/Shared/Progress.tsx b/src/components/tables/RowActions/Shared/Progress.tsx index f5dbe5155..ae7b55226 100644 --- a/src/components/tables/RowActions/Shared/Progress.tsx +++ b/src/components/tables/RowActions/Shared/Progress.tsx @@ -9,27 +9,8 @@ import { import ErrorLogs from 'components/shared/Entity/Error/Logs'; import Error from 'components/shared/Error'; import { CheckCircle, InfoCircle, WarningCircle } from 'iconoir-react'; -import { ReactNode } from 'react'; import { FormattedMessage } from 'react-intl'; - -export enum ProgressStates { - RUNNING = 1, - SKIPPED = 2, - FAILED = 3, - SUCCESS = 4, -} - -export interface SharedProgressProps { - name: string; - error: any | null; - logToken?: string | null; - renderError?: (error: any, progressState: ProgressStates) => ReactNode; - renderLogs?: Function | boolean; - skippedMessageID?: string; - runningMessageID: string; - successMessageID: string; - state: ProgressStates; -} +import { ProgressStates, SharedProgressProps } from './types'; const wrapperStyling = { mb: 1, ml: 3, width: '100%' }; diff --git a/src/components/tables/RowActions/Shared/UpdateEntity.tsx b/src/components/tables/RowActions/Shared/UpdateEntity.tsx index db453694d..b02b8f0ac 100644 --- a/src/components/tables/RowActions/Shared/UpdateEntity.tsx +++ b/src/components/tables/RowActions/Shared/UpdateEntity.tsx @@ -8,10 +8,6 @@ import { createPublication } from 'api/publications'; import AlertBox from 'components/shared/AlertBox'; import DraftErrors from 'components/shared/Entity/Error/DraftErrors'; import Error from 'components/shared/Error'; -import SharedProgress, { - ProgressStates, - SharedProgressProps, -} from 'components/tables/RowActions/Shared/Progress'; import { useZustandStore } from 'context/Zustand/provider'; import { LiveSpecsExtQueryWithSpec, @@ -26,6 +22,8 @@ import { selectableTableStoreSelectors, } from 'stores/Tables/Store'; import { Entity } from 'types'; +import SharedProgress from './Progress'; +import { ProgressStates, SharedProgressProps } from './types'; export interface UpdateEntityProps { entity: CaptureQuery; diff --git a/src/components/tables/RowActions/Shared/types.ts b/src/components/tables/RowActions/Shared/types.ts new file mode 100644 index 000000000..0896d8ea5 --- /dev/null +++ b/src/components/tables/RowActions/Shared/types.ts @@ -0,0 +1,22 @@ +import { ReactNode } from 'react'; + +export enum ProgressStates { + IDLE = -1, // new for steps + PAUSED = 0, // Not used + RUNNING = 1, + SKIPPED = 2, + FAILED = 3, + SUCCESS = 4, +} + +export interface SharedProgressProps { + name: string; + error: any | null; + logToken?: string | null; + renderError?: (error: any, progressState: ProgressStates) => ReactNode; + renderLogs?: Function | boolean; + skippedMessageID?: string; + runningMessageID: string; + successMessageID: string; + state: ProgressStates; +} diff --git a/src/services/supabase.ts b/src/services/supabase.ts index 9314b07bc..0f9423ab1 100644 --- a/src/services/supabase.ts +++ b/src/services/supabase.ts @@ -310,10 +310,10 @@ export function invokeSupabase<T>(fn: FUNCTIONS, body: any) { ); } -export const insertSupabase = ( +export const insertSupabase = <T = any>( table: TABLES, data: any -): PromiseLike<CallSupabaseResponse<any>> => { +): PromiseLike<CallSupabaseResponse<T>> => { return supabaseRetry( () => supabaseClient @@ -321,7 +321,7 @@ export const insertSupabase = ( .insert(Array.isArray(data) ? data : [data]) .select(), 'insert' - ).then(handleSuccess, handleFailure); + ).then(handleSuccess<T>, handleFailure); }; // Makes update calls. Mainly consumed in the src/api folder From f820b365a94f71be2e2d869d9868fc8463a31274 Mon Sep 17 00:00:00 2001 From: Travis Jenkins <travis@estuary.dev> Date: Tue, 17 Sep 2024 17:35:49 -0400 Subject: [PATCH 07/66] typing tweaks handling errors a bit better updating enum --- .../Entity/prompts/PreSave/Content/index.tsx | 79 ++++++++++++++----- .../dataFlowReset/DisableCapture/index.tsx | 14 +++- .../shared/Entity/prompts/store/shared.ts | 2 +- src/components/shared/Entity/prompts/types.ts | 3 +- .../tables/RowActions/Shared/types.ts | 8 +- 5 files changed, 77 insertions(+), 29 deletions(-) diff --git a/src/components/shared/Entity/prompts/PreSave/Content/index.tsx b/src/components/shared/Entity/prompts/PreSave/Content/index.tsx index c85a8a384..194c309e3 100644 --- a/src/components/shared/Entity/prompts/PreSave/Content/index.tsx +++ b/src/components/shared/Entity/prompts/PreSave/Content/index.tsx @@ -7,9 +7,14 @@ import { Stepper, } from '@mui/material'; import ErrorBoundryWrapper from 'components/shared/ErrorBoundryWrapper'; -import { ProgressStates } from 'components/tables/RowActions/Shared/types'; +import { + ProgressFinished, + ProgressStates, +} from 'components/tables/RowActions/Shared/types'; import { useMemo } from 'react'; import { useIntl } from 'react-intl'; +import Error from 'components/shared/Error'; +import ErrorLogs from 'components/shared/Entity/Error/Logs'; import { usePreSavePromptStore } from '../../store/usePreSavePromptStore'; function Content() { @@ -22,25 +27,57 @@ function Content() { const renderedSteps = useMemo( () => steps ? ( - steps.map((step, index) => { - const { StepComponent, stepLabelMessageId, state } = step; - return ( - <Step key={`PreSave-step-${index}`}> - <StepLabel> - {intl.formatMessage({ id: stepLabelMessageId })} - </StepLabel> - <StepContent> - <ErrorBoundryWrapper> - {state.progress === - ProgressStates.RUNNING ? ( - <LinearProgress /> - ) : null} - <StepComponent stepIndex={index} /> - </ErrorBoundryWrapper> - </StepContent> - </Step> - ); - }) + steps.map( + ( + { + StepComponent, + stepLabelMessageId, + state: { error, progress, logsToken }, + }, + index + ) => { + return ( + <Step + key={`PreSave-step-${index}`} + completed={progress >= ProgressFinished} + > + <StepLabel error={Boolean(error)}> + {intl.formatMessage({ + id: stepLabelMessageId, + })} + </StepLabel> + <StepContent> + <ErrorBoundryWrapper> + {progress === ProgressStates.RUNNING ? ( + <LinearProgress /> + ) : null} + + <Error + severity="error" + error={error} + condensed + hideTitle + /> + + <ErrorLogs + defaultOpen + logToken={ + Boolean(error) + ? logsToken + : null + } + logProps={{ + fetchAll: true, + }} + /> + + <StepComponent stepIndex={index} /> + </ErrorBoundryWrapper> + </StepContent> + </Step> + ); + } + ) ) : ( <LinearProgress /> ), @@ -49,7 +86,7 @@ function Content() { return ( <DialogContent> - <Stepper orientation="vertical" activeStep={activeStep}> + <Stepper orientation="vertical" activeStep={activeStep} nonLinear> {renderedSteps} </Stepper> </DialogContent> diff --git a/src/components/shared/Entity/prompts/steps/dataFlowReset/DisableCapture/index.tsx b/src/components/shared/Entity/prompts/steps/dataFlowReset/DisableCapture/index.tsx index 019b22934..c7455c2a8 100644 --- a/src/components/shared/Entity/prompts/steps/dataFlowReset/DisableCapture/index.tsx +++ b/src/components/shared/Entity/prompts/steps/dataFlowReset/DisableCapture/index.tsx @@ -40,7 +40,10 @@ function DisableCapture({ stepIndex }: StepComponentProps) { ); if (updateResponse.error) { - console.log('uh oh'); + updateStep({ + error: updateResponse.error, + progress: ProgressStates.FAILED, + }); return; } @@ -48,7 +51,10 @@ function DisableCapture({ stepIndex }: StepComponentProps) { const publishResponse = await createPublication(draftId, false); if (publishResponse.error || !publishResponse.data) { - console.log('uh oh'); + updateStep({ + error: publishResponse.error, + progress: ProgressStates.FAILED, + }); return; } @@ -68,7 +74,7 @@ function DisableCapture({ stepIndex }: StepComponentProps) { }, async (error: any) => { updateStep({ - errors: error, + error, progress: ProgressStates.FAILED, valid: false, }); @@ -82,7 +88,7 @@ function DisableCapture({ stepIndex }: StepComponentProps) { }); return ( - <>explain what we're doing</> + <>explain what is happening</> // <Logs // token={logsToken} // height={350} diff --git a/src/components/shared/Entity/prompts/store/shared.ts b/src/components/shared/Entity/prompts/store/shared.ts index 4b0b668fb..bb65eab60 100644 --- a/src/components/shared/Entity/prompts/store/shared.ts +++ b/src/components/shared/Entity/prompts/store/shared.ts @@ -2,7 +2,7 @@ import { ProgressStates } from 'components/tables/RowActions/Shared/types'; import { PromptStepState } from '../types'; export const defaultStepState: PromptStepState = { - errors: null, + error: null, progress: ProgressStates.IDLE, started: false, valid: false, diff --git a/src/components/shared/Entity/prompts/types.ts b/src/components/shared/Entity/prompts/types.ts index 1577614d5..f85ebbf6d 100644 --- a/src/components/shared/Entity/prompts/types.ts +++ b/src/components/shared/Entity/prompts/types.ts @@ -1,9 +1,10 @@ import { EmotionJSX } from '@emotion/react/types/jsx-namespace'; +import { PostgrestError } from '@supabase/postgrest-js'; import { ProgressStates } from 'components/tables/RowActions/Shared/types'; export interface PromptStepState { // Both server and client side error - errors: any[] | null; + error: PostgrestError | null; // Stores what the step is currently doing progress: ProgressStates; diff --git a/src/components/tables/RowActions/Shared/types.ts b/src/components/tables/RowActions/Shared/types.ts index 0896d8ea5..508c7acc0 100644 --- a/src/components/tables/RowActions/Shared/types.ts +++ b/src/components/tables/RowActions/Shared/types.ts @@ -1,12 +1,16 @@ import { ReactNode } from 'react'; +export const ProgressFinished = 200; + export enum ProgressStates { IDLE = -1, // new for steps PAUSED = 0, // Not used RUNNING = 1, SKIPPED = 2, - FAILED = 3, - SUCCESS = 4, + + // Making these high numbers so we have room to put extras between + SUCCESS = 200, + FAILED = 400, } export interface SharedProgressProps { From 38ee682331c00516c546782c0d97aac96ee0e046 Mon Sep 17 00:00:00 2001 From: Travis Jenkins <travis@estuary.dev> Date: Tue, 17 Sep 2024 18:46:30 -0400 Subject: [PATCH 08/66] Storing off step definition --- .../DisableCapture/definition.ts | 14 +++++ .../dataFlowReset/EnableCapture/definition.ts | 14 +++++ .../SelectMaterialization/definition.ts | 17 +++++++ .../UpdateMaterialization/definition.ts | 12 +++++ .../WaitForCaptureStop/definition.ts | 12 +++++ .../prompts/steps/dataFlowReset/shared.ts | 51 +++++-------------- .../steps/preSave/ChangeReview/definition.ts | 13 +++++ .../steps/preSave/Publish/definition.ts | 10 ++++ 8 files changed, 106 insertions(+), 37 deletions(-) create mode 100644 src/components/shared/Entity/prompts/steps/dataFlowReset/DisableCapture/definition.ts create mode 100644 src/components/shared/Entity/prompts/steps/dataFlowReset/EnableCapture/definition.ts create mode 100644 src/components/shared/Entity/prompts/steps/dataFlowReset/SelectMaterialization/definition.ts create mode 100644 src/components/shared/Entity/prompts/steps/dataFlowReset/UpdateMaterialization/definition.ts create mode 100644 src/components/shared/Entity/prompts/steps/dataFlowReset/WaitForCaptureStop/definition.ts create mode 100644 src/components/shared/Entity/prompts/steps/preSave/ChangeReview/definition.ts create mode 100644 src/components/shared/Entity/prompts/steps/preSave/Publish/definition.ts diff --git a/src/components/shared/Entity/prompts/steps/dataFlowReset/DisableCapture/definition.ts b/src/components/shared/Entity/prompts/steps/dataFlowReset/DisableCapture/definition.ts new file mode 100644 index 000000000..2509843ed --- /dev/null +++ b/src/components/shared/Entity/prompts/steps/dataFlowReset/DisableCapture/definition.ts @@ -0,0 +1,14 @@ +import { defaultStepState } from '../../../store/shared'; +import { PromptStep } from '../../../types'; +import DisableCapture from '.'; + +export const DisableCaptureStep: PromptStep<{ + logsToken: string | null; +}> = { + StepComponent: DisableCapture, + stepLabelMessageId: 'dataFlowReset.disableCapture.title', + state: defaultStepState, + context: { + logsToken: null, + }, +}; diff --git a/src/components/shared/Entity/prompts/steps/dataFlowReset/EnableCapture/definition.ts b/src/components/shared/Entity/prompts/steps/dataFlowReset/EnableCapture/definition.ts new file mode 100644 index 000000000..aa3055ae9 --- /dev/null +++ b/src/components/shared/Entity/prompts/steps/dataFlowReset/EnableCapture/definition.ts @@ -0,0 +1,14 @@ +import { defaultStepState } from '../../../store/shared'; +import { PromptStep } from '../../../types'; +import EnableCapture from '.'; + +export const EnableCaptureStep: PromptStep<{ + logsToken: string | null; +}> = { + StepComponent: EnableCapture, + stepLabelMessageId: 'dataFlowReset.enableCapture.title', + state: defaultStepState, + context: { + logsToken: null, + }, +}; diff --git a/src/components/shared/Entity/prompts/steps/dataFlowReset/SelectMaterialization/definition.ts b/src/components/shared/Entity/prompts/steps/dataFlowReset/SelectMaterialization/definition.ts new file mode 100644 index 000000000..76abe504e --- /dev/null +++ b/src/components/shared/Entity/prompts/steps/dataFlowReset/SelectMaterialization/definition.ts @@ -0,0 +1,17 @@ +import { defaultStepState } from '../../../store/shared'; +import { PromptStep } from '../../../types'; +import SelectMaterialization from '.'; + +export const SelectMaterializationStep: PromptStep<{ + backfillTarget: string | null; +}> = { + StepComponent: SelectMaterialization, + stepLabelMessageId: 'dataFlowReset.selectMaterialization.title', + state: { + ...defaultStepState, + valid: false, + }, + context: { + backfillTarget: null, + }, +}; diff --git a/src/components/shared/Entity/prompts/steps/dataFlowReset/UpdateMaterialization/definition.ts b/src/components/shared/Entity/prompts/steps/dataFlowReset/UpdateMaterialization/definition.ts new file mode 100644 index 000000000..968fd5c75 --- /dev/null +++ b/src/components/shared/Entity/prompts/steps/dataFlowReset/UpdateMaterialization/definition.ts @@ -0,0 +1,12 @@ +import { defaultStepState } from '../../../store/shared'; +import { PromptStep } from '../../../types'; +import UpdateMaterialization from '.'; + +export const UpdateMaterializationStep: PromptStep<null> = { + StepComponent: UpdateMaterialization, + stepLabelMessageId: 'dataFlowReset.updateMaterialization.title', + state: { + ...defaultStepState, + }, + context: null, +}; diff --git a/src/components/shared/Entity/prompts/steps/dataFlowReset/WaitForCaptureStop/definition.ts b/src/components/shared/Entity/prompts/steps/dataFlowReset/WaitForCaptureStop/definition.ts new file mode 100644 index 000000000..2102f39a5 --- /dev/null +++ b/src/components/shared/Entity/prompts/steps/dataFlowReset/WaitForCaptureStop/definition.ts @@ -0,0 +1,12 @@ +import { defaultStepState } from '../../../store/shared'; +import { PromptStep } from '../../../types'; +import WaitForCapture from '.'; + +export const WaitForCaptureStep: PromptStep<null> = { + StepComponent: WaitForCapture, + stepLabelMessageId: 'dataFlowReset.waitForCapture.title', + state: { + ...defaultStepState, + }, + context: null, +}; diff --git a/src/components/shared/Entity/prompts/steps/dataFlowReset/shared.ts b/src/components/shared/Entity/prompts/steps/dataFlowReset/shared.ts index f2bd7cbec..a49f7da99 100644 --- a/src/components/shared/Entity/prompts/steps/dataFlowReset/shared.ts +++ b/src/components/shared/Entity/prompts/steps/dataFlowReset/shared.ts @@ -1,40 +1,17 @@ -import { defaultStepState } from '../../store/shared'; -import { PromptStep } from '../../types'; -import DisableCapture from './DisableCapture'; -import EnableCapture from './EnableCapture'; -import SelectMaterialization from './SelectMaterialization'; -import MarkMaterialization from './UpdateMaterialization'; -import WaitForCaptureStop from './WaitForCaptureStop'; +import { DisableCaptureStep } from './DisableCapture/definition'; +import { EnableCaptureStep } from './EnableCapture/definition'; +import { SelectMaterializationStep } from './SelectMaterialization/definition'; +import { UpdateMaterializationStep } from './UpdateMaterialization/definition'; +import { WaitForCaptureStep } from './WaitForCaptureStop/definition'; + +export const DataFlowSteps = { + selectMaterialization: SelectMaterializationStep, + disableCapture: DisableCaptureStep, + waitForCapture: WaitForCaptureStep, + updateMaterialization: UpdateMaterializationStep, + enableCapture: EnableCaptureStep, +}; // !!!!!!!!!ORDER IS IMPORTANT!!!!!!!!!!!! // We run through steps in order -export const DataFlowResetSteps: PromptStep[] = [ - { - StepComponent: SelectMaterialization, - stepLabelMessageId: 'dataFlowReset.selectMaterialization.title', - state: { - ...defaultStepState, - valid: false, - }, - }, - { - StepComponent: DisableCapture, - stepLabelMessageId: 'dataFlowReset.disableCapture.title', - state: defaultStepState, - }, - { - StepComponent: WaitForCaptureStop, - stepLabelMessageId: 'dataFlowReset.waitForCapture.title', - state: defaultStepState, - }, - { - StepComponent: MarkMaterialization, - stepLabelMessageId: 'dataFlowReset.markMaterialization.title', - state: defaultStepState, - }, - { - StepComponent: EnableCapture, - stepLabelMessageId: 'dataFlowReset.enableCapture.title', - state: defaultStepState, - }, -]; +export const DataFlowResetSteps = Object.values(DataFlowSteps); diff --git a/src/components/shared/Entity/prompts/steps/preSave/ChangeReview/definition.ts b/src/components/shared/Entity/prompts/steps/preSave/ChangeReview/definition.ts new file mode 100644 index 000000000..84fcb32f5 --- /dev/null +++ b/src/components/shared/Entity/prompts/steps/preSave/ChangeReview/definition.ts @@ -0,0 +1,13 @@ +import { defaultStepState } from '../../../store/shared'; +import { PromptStep } from '../../../types'; +import ChangeReview from '.'; + +export const ChangeReviewStep: PromptStep<null> = { + StepComponent: ChangeReview, + stepLabelMessageId: 'preSavePrompt.changeReview.title', + state: { + ...defaultStepState, + valid: true, + }, + context: null, +}; diff --git a/src/components/shared/Entity/prompts/steps/preSave/Publish/definition.ts b/src/components/shared/Entity/prompts/steps/preSave/Publish/definition.ts new file mode 100644 index 000000000..7c6a0a92e --- /dev/null +++ b/src/components/shared/Entity/prompts/steps/preSave/Publish/definition.ts @@ -0,0 +1,10 @@ +import { defaultStepState } from '../../../store/shared'; +import { PromptStep } from '../../../types'; +import Publish from '.'; + +export const PublishStep: PromptStep<null> = { + StepComponent: Publish, + stepLabelMessageId: 'preSavePrompt.publish.title', + state: defaultStepState, + context: null, +}; From 6ffd672ac5376b72702a2d112837b8a4e14caa2b Mon Sep 17 00:00:00 2001 From: Travis Jenkins <travis@estuary.dev> Date: Tue, 17 Sep 2024 18:46:52 -0400 Subject: [PATCH 09/66] Updating key to keep consistent --- src/lang/en-US/Workflows.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lang/en-US/Workflows.ts b/src/lang/en-US/Workflows.ts index 94e74a7bf..95211b3cd 100644 --- a/src/lang/en-US/Workflows.ts +++ b/src/lang/en-US/Workflows.ts @@ -136,7 +136,7 @@ export const Workflows: Record<string, string> = { 'dataFlowReset.selectMaterialization.title': `Select materialization for data flow reset`, 'dataFlowReset.disableCapture.title': `Disable capture`, 'dataFlowReset.waitForCapture.title': `Wait for capture to fully stop`, - 'dataFlowReset.markMaterialization.title': `Update Materialization`, + 'dataFlowReset.updateMaterialization.title': `Update Materialization`, 'dataFlowReset.enableCapture.title': `Enable capture`, // Dataflow reset From afebcf9cacd197aec6cadf3648e98f8f627abede Mon Sep 17 00:00:00 2001 From: Travis Jenkins <travis@estuary.dev> Date: Tue, 17 Sep 2024 18:47:10 -0400 Subject: [PATCH 10/66] Adding support for a context to store data... but this feels wrong --- src/components/shared/Entity/prompts/types.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/shared/Entity/prompts/types.ts b/src/components/shared/Entity/prompts/types.ts index f85ebbf6d..8f2cb1c4f 100644 --- a/src/components/shared/Entity/prompts/types.ts +++ b/src/components/shared/Entity/prompts/types.ts @@ -23,8 +23,9 @@ export interface StepComponentProps { stepIndex: number; } -export interface PromptStep { +export interface PromptStep<T = any> { StepComponent: (props: StepComponentProps) => EmotionJSX.Element; stepLabelMessageId: string; state: PromptStepState; + context: T; } From d5b94e1753022e2bb10f923ab6f1653c62b0f7b8 Mon Sep 17 00:00:00 2001 From: Travis Jenkins <travis@estuary.dev> Date: Tue, 17 Sep 2024 18:47:49 -0400 Subject: [PATCH 11/66] adding quick support to store off things in a keyed / machine way --- .../prompts/store/usePreSavePromptStore.ts | 75 ++++++++++++------- 1 file changed, 50 insertions(+), 25 deletions(-) diff --git a/src/components/shared/Entity/prompts/store/usePreSavePromptStore.ts b/src/components/shared/Entity/prompts/store/usePreSavePromptStore.ts index 4fab54055..2b9da7a0d 100644 --- a/src/components/shared/Entity/prompts/store/usePreSavePromptStore.ts +++ b/src/components/shared/Entity/prompts/store/usePreSavePromptStore.ts @@ -5,17 +5,23 @@ import { devtoolsOptions } from 'utils/store-utils'; import { useShallow } from 'zustand/react/shallow'; import { ProgressStates } from 'components/tables/RowActions/Shared/types'; import { PromptStep, PromptStepState } from '../types'; -import ChangeReview from '../steps/preSave/ChangeReview'; -import { DataFlowResetSteps } from '../steps/dataFlowReset/shared'; -import Publish from '../steps/preSave/Publish'; -import { defaultStepState } from './shared'; +import { + DataFlowResetSteps, + DataFlowSteps, +} from '../steps/dataFlowReset/shared'; +import { ChangeReviewStep } from '../steps/preSave/ChangeReview/definition'; +import { PublishStep } from '../steps/preSave/Publish/definition'; interface PreSavePromptStore { steps: PromptStep[] | null; - updateStep: (settings: Partial<PromptStepState>, step?: number) => void; + machine: any; + updateMachine: (key: string, settings: Partial<PromptStep>) => void; + + updateStep: (step: number, settings: Partial<PromptStepState>) => void; initializeSteps: (backfillEnabled: boolean) => void; activeStep: number; + activeMachine: string; setActiveStep: (val: PreSavePromptStore['activeStep']) => void; nextStep: () => void; previousStep: () => void; @@ -28,11 +34,13 @@ interface PreSavePromptStore { const getInitialState = (): Pick< PreSavePromptStore, - 'activeStep' | 'steps' | 'show' + 'activeStep' | 'activeMachine' | 'steps' | 'show' | 'machine' > => ({ activeStep: 0, + activeMachine: 'changeReview', show: false, steps: null, + machine: {}, }); const name = 'estuary.presave-prompt-store'; @@ -46,42 +54,39 @@ export const usePreSavePromptStore = create<PreSavePromptStore>()( initializeSteps: (backfillEnabled) => set( produce((state: PreSavePromptStore) => { - const newSteps: PromptStep[] = [ - { - StepComponent: ChangeReview, - stepLabelMessageId: - 'preSavePrompt.changeReview.title', - state: { - ...defaultStepState, - valid: true, - }, - }, - ]; + const newSteps: PromptStep[] = [ChangeReviewStep]; if (backfillEnabled) { newSteps.push(...DataFlowResetSteps); } - newSteps.push({ - StepComponent: Publish, - stepLabelMessageId: 'preSavePrompt.publish.title', - state: defaultStepState, - }); + newSteps.push(PublishStep); state.steps = newSteps; + + if (backfillEnabled) { + state.machine = { + changeReview: ChangeReviewStep, + ...DataFlowSteps, + saveAndPublish: PublishStep, + }; + } else { + state.machine = { + changeReview: ChangeReviewStep, + saveAndPublish: PublishStep, + }; + } }), false, 'initializeSteps' ), - updateStep: (settings, step) => + updateStep: (stepToUpdate, settings) => set( produce((state: PreSavePromptStore) => { if (!state.steps) { return; } - const stepToUpdate = step ?? state.activeStep; - state.steps[stepToUpdate].state = { ...state.steps[stepToUpdate].state, ...settings, @@ -91,6 +96,22 @@ export const usePreSavePromptStore = create<PreSavePromptStore>()( 'setActiveStep' ), + updateMachine: (machineToUpdate, settings) => + set( + produce((state: PreSavePromptStore) => { + if (!state.machine) { + return; + } + + state.machine[machineToUpdate] = { + ...state.machine[machineToUpdate], + ...settings, + }; + }), + false, + 'updateMachine' + ), + setActiveStep: (value) => set( produce((state: PreSavePromptStore) => { @@ -130,6 +151,10 @@ export const usePreSavePromptStore = create<PreSavePromptStore>()( set( produce((state: PreSavePromptStore) => { state.show = val; + + if (!val) { + state.resetState(); + } }), false, 'setShow' From 1cf48d3dab56ca812b34b5178e10aa89d57ba2fa Mon Sep 17 00:00:00 2001 From: Travis Jenkins <travis@estuary.dev> Date: Tue, 17 Sep 2024 18:48:06 -0400 Subject: [PATCH 12/66] cleaning up handled as we are not using this --- .../Entity/hooks/useDataFlowResetHandler.tsx | 40 ------------------- 1 file changed, 40 deletions(-) delete mode 100644 src/components/shared/Entity/hooks/useDataFlowResetHandler.tsx diff --git a/src/components/shared/Entity/hooks/useDataFlowResetHandler.tsx b/src/components/shared/Entity/hooks/useDataFlowResetHandler.tsx deleted file mode 100644 index d8c24cfbc..000000000 --- a/src/components/shared/Entity/hooks/useDataFlowResetHandler.tsx +++ /dev/null @@ -1,40 +0,0 @@ -import { createPublication } from 'api/publications'; -import { useEditorStore_id } from 'components/editor/Store/hooks'; -import { useCallback } from 'react'; -import { useBindingStore } from 'stores/Binding/Store'; -import { generateDisabledSpec } from 'utils/entity-utils'; - -function useDataFlowResetHandler() { - const [backfillDataFlowTarget] = useBindingStore((state) => [ - state.backfillDataFlowTarget, - ]); - - const draftId = useEditorStore_id(); - - return useCallback(async () => { - console.log('Starting'); - - // Capture - Disable - generateDisabledSpec({}, false, false); - - // Capture - Publish - const publishResponse = await createPublication(draftId, false); - if (publishResponse.error) { - console.log('publishResponse.error', publishResponse.error); - // return failed(publishResponse); - } - - // waitForPublishToFinish(publishResponse.data[0].id, false); - - // Runtime must stop 100% and is done writing documents (wait for publication to succeed and then wait… or keep looking for shards) - // (IMPORTANT) - save current time - // Bindings - update backfill property - // Capture - enable - // Materialization - update notBefore property - // Capture & Materialization - Publish - - console.log('backfillDataFlowTarget', backfillDataFlowTarget); - }, [backfillDataFlowTarget, draftId]); -} - -export default useDataFlowResetHandler; From 1cf6db5d00697e257c391cc525b3426e4938997c Mon Sep 17 00:00:00 2001 From: Travis Jenkins <travis@estuary.dev> Date: Tue, 17 Sep 2024 18:48:24 -0400 Subject: [PATCH 13/66] Forcing to pass an index when updating a step --- .../steps/dataFlowReset/DisableCapture/index.tsx | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/components/shared/Entity/prompts/steps/dataFlowReset/DisableCapture/index.tsx b/src/components/shared/Entity/prompts/steps/dataFlowReset/DisableCapture/index.tsx index c7455c2a8..21371f8c0 100644 --- a/src/components/shared/Entity/prompts/steps/dataFlowReset/DisableCapture/index.tsx +++ b/src/components/shared/Entity/prompts/steps/dataFlowReset/DisableCapture/index.tsx @@ -17,6 +17,7 @@ function DisableCapture({ stepIndex }: StepComponentProps) { const { jobStatusPoller } = useJobStatusPoller(); const thisStep = usePreSavePromptStore((state) => state.steps?.[stepIndex]); + const [updateStep, nextStep] = usePreSavePromptStore((state) => [ state.updateStep, state.nextStep, @@ -24,7 +25,7 @@ function DisableCapture({ stepIndex }: StepComponentProps) { useMount(() => { if (thisStep?.state.progress === ProgressStates.IDLE) { - updateStep({ + updateStep(stepIndex, { progress: ProgressStates.RUNNING, }); @@ -40,7 +41,7 @@ function DisableCapture({ stepIndex }: StepComponentProps) { ); if (updateResponse.error) { - updateStep({ + updateStep(stepIndex, { error: updateResponse.error, progress: ProgressStates.FAILED, }); @@ -51,21 +52,21 @@ function DisableCapture({ stepIndex }: StepComponentProps) { const publishResponse = await createPublication(draftId, false); if (publishResponse.error || !publishResponse.data) { - updateStep({ + updateStep(stepIndex, { error: publishResponse.error, progress: ProgressStates.FAILED, }); return; } - updateStep({ + updateStep(stepIndex, { logsToken: publishResponse.data[0].logs_token, }); jobStatusPoller( getPublicationByIdQuery(publishResponse.data[0].id), async () => { - updateStep({ + updateStep(stepIndex, { progress: ProgressStates.SUCCESS, valid: true, }); @@ -73,7 +74,7 @@ function DisableCapture({ stepIndex }: StepComponentProps) { nextStep(); }, async (error: any) => { - updateStep({ + updateStep(stepIndex, { error, progress: ProgressStates.FAILED, valid: false, From 2ce4b596f698f09f89f150e1196722545f8702ea Mon Sep 17 00:00:00 2001 From: Travis Jenkins <travis@estuary.dev> Date: Tue, 17 Sep 2024 18:49:16 -0400 Subject: [PATCH 14/66] quick hack and typing update --- .../dataFlowReset/SelectMaterialization/Materializations.tsx | 2 +- .../prompts/steps/dataFlowReset/SelectMaterialization/types.ts | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/components/shared/Entity/prompts/steps/dataFlowReset/SelectMaterialization/Materializations.tsx b/src/components/shared/Entity/prompts/steps/dataFlowReset/SelectMaterialization/Materializations.tsx index b727e49b3..e3657285d 100644 --- a/src/components/shared/Entity/prompts/steps/dataFlowReset/SelectMaterialization/Materializations.tsx +++ b/src/components/shared/Entity/prompts/steps/dataFlowReset/SelectMaterialization/Materializations.tsx @@ -23,7 +23,7 @@ function Materializations({ selected }: BindingReviewProps) { ]); useEffect(() => { - updateStep({ + updateStep(1, { valid: Boolean( (!isValidating && !foundData) || backfillDataFlowTarget ), diff --git a/src/components/shared/Entity/prompts/steps/dataFlowReset/SelectMaterialization/types.ts b/src/components/shared/Entity/prompts/steps/dataFlowReset/SelectMaterialization/types.ts index b2e63da83..28a1e11a2 100644 --- a/src/components/shared/Entity/prompts/steps/dataFlowReset/SelectMaterialization/types.ts +++ b/src/components/shared/Entity/prompts/steps/dataFlowReset/SelectMaterialization/types.ts @@ -4,6 +4,8 @@ export interface BindingReviewProps { selected: string[]; } +export type MaterializationsProps = BindingReviewProps; + export interface RelatedMaterializationSelectorProps { keys: LiveSpecsExt_Related[]; value: string | null; From cf48496911f384a1e1a8596cd20e5861436a22a1 Mon Sep 17 00:00:00 2001 From: Travis Jenkins <travis@estuary.dev> Date: Wed, 18 Sep 2024 15:36:47 -0400 Subject: [PATCH 15/66] Checking locked to skip showing the load bar Closing when "back" button is clicked on first step No longer making steps options just defaulting to empty Just setting show to false is enough for closing dialog Checking the shallow `onFirstStep` to reduce renders Moving context out to the main store Only running initialize once per hydration --- src/components/shared/Entity/Header.tsx | 3 +- .../shared/Entity/prompts/PreSave/Actions.tsx | 26 +++-- .../Entity/prompts/PreSave/Content/index.tsx | 61 ++++++------ .../shared/Entity/prompts/PreSave/Title.tsx | 19 ++-- .../shared/Entity/prompts/PreSave/index.tsx | 13 +-- .../DisableCapture/definition.ts | 11 +-- .../dataFlowReset/DisableCapture/index.tsx | 34 ++++--- .../dataFlowReset/EnableCapture/definition.ts | 7 +- .../Materializations.tsx | 18 +--- .../SelectMaterialization/Selector.tsx | 28 +++++- .../SelectMaterialization/definition.ts | 11 +-- .../UpdateMaterialization/definition.ts | 7 +- .../UpdateMaterialization/index.tsx | 98 ++++++++++++++++++- .../WaitForCaptureStop/definition.ts | 8 +- .../WaitForCaptureStop/index.tsx | 71 +++++++++++++- .../steps/preSave/ChangeReview/definition.ts | 3 +- .../steps/preSave/Publish/definition.ts | 3 +- .../shared/Entity/prompts/store/Hydrator.tsx | 19 ++-- .../shared/Entity/prompts/store/types.ts | 23 +++++ .../prompts/store/usePreSavePromptStore.ts | 93 ++++++------------ src/components/shared/Entity/prompts/types.ts | 10 +- src/context/LoopIndex/index.tsx | 13 +++ src/context/LoopIndex/shared.ts | 3 + src/context/LoopIndex/types.ts | 5 + src/context/LoopIndex/useLoopIndex.tsx | 14 +++ src/stores/FormState/Store.ts | 19 +++- src/stores/FormState/types.ts | 3 + 27 files changed, 424 insertions(+), 199 deletions(-) create mode 100644 src/components/shared/Entity/prompts/store/types.ts create mode 100644 src/context/LoopIndex/index.tsx create mode 100644 src/context/LoopIndex/shared.ts create mode 100644 src/context/LoopIndex/types.ts create mode 100644 src/context/LoopIndex/useLoopIndex.tsx diff --git a/src/components/shared/Entity/Header.tsx b/src/components/shared/Entity/Header.tsx index 542978f76..d0d601c76 100644 --- a/src/components/shared/Entity/Header.tsx +++ b/src/components/shared/Entity/Header.tsx @@ -48,6 +48,7 @@ function EntityToolbar({ const formStatus = useFormStateStore_status(); const discovering = !draftId && formStatus === FormStatus.GENERATING; const saved = formStatus === FormStatus.SAVED; + const locked = formStatus === FormStatus.LOCKED; const PrimaryButton = PrimaryButtonComponent ?? EntitySaveButton; const SecondaryButton = SecondaryButtonComponent ?? EntityTestButton; @@ -90,7 +91,7 @@ function EntityToolbar({ }} > <Fade - in={Boolean(formActive && !saved)} + in={Boolean(formActive && !saved && !locked)} mountOnEnter unmountOnExit > diff --git a/src/components/shared/Entity/prompts/PreSave/Actions.tsx b/src/components/shared/Entity/prompts/PreSave/Actions.tsx index fc00c6a1e..6b7226977 100644 --- a/src/components/shared/Entity/prompts/PreSave/Actions.tsx +++ b/src/components/shared/Entity/prompts/PreSave/Actions.tsx @@ -2,36 +2,42 @@ import { Button, DialogActions, Stack } from '@mui/material'; import { useIntl } from 'react-intl'; import { usePreSavePromptStore, - usePreSavePromptStore_activeStep, usePreSavePromptStore_onFirstStep, + usePreSavePromptStore_stepValid, } from '../store/usePreSavePromptStore'; function Actions() { const intl = useIntl(); - const [nextStep, previousStep] = usePreSavePromptStore((state) => [ - state.nextStep, - state.previousStep, - ]); + const [activeStep, nextStep, previousStep, setShow] = usePreSavePromptStore( + (state) => [ + state.activeStep, + state.nextStep, + state.previousStep, + state.setShow, + ] + ); - const activeStep = usePreSavePromptStore_activeStep(); + const canContinue = usePreSavePromptStore_stepValid; const onFirstStep = usePreSavePromptStore_onFirstStep(); return ( <DialogActions> <Stack direction="row" spacing={2}> <Button - onClick={previousStep} + disabled={activeStep > 2} + onClick={onFirstStep ? () => setShow(false) : previousStep} variant="text" - disabled={onFirstStep} > - {intl.formatMessage({ id: 'cta.back' })} + {intl.formatMessage({ + id: onFirstStep ? 'cta.close' : 'cta.back', + })} </Button> <Button onClick={nextStep} variant="outlined" - disabled={!Boolean(activeStep?.valid)} + disabled={!canContinue} > {intl.formatMessage({ id: 'cta.continue' })} </Button> diff --git a/src/components/shared/Entity/prompts/PreSave/Content/index.tsx b/src/components/shared/Entity/prompts/PreSave/Content/index.tsx index 194c309e3..4b3ed8b61 100644 --- a/src/components/shared/Entity/prompts/PreSave/Content/index.tsx +++ b/src/components/shared/Entity/prompts/PreSave/Content/index.tsx @@ -15,6 +15,7 @@ import { useMemo } from 'react'; import { useIntl } from 'react-intl'; import Error from 'components/shared/Error'; import ErrorLogs from 'components/shared/Entity/Error/Logs'; +import { LoopIndexContextProvider } from 'context/LoopIndex'; import { usePreSavePromptStore } from '../../store/usePreSavePromptStore'; function Content() { @@ -26,28 +27,28 @@ function Content() { const renderedSteps = useMemo( () => - steps ? ( - steps.map( - ( - { - StepComponent, - stepLabelMessageId, - state: { error, progress, logsToken }, - }, - index - ) => { - return ( - <Step - key={`PreSave-step-${index}`} - completed={progress >= ProgressFinished} - > - <StepLabel error={Boolean(error)}> - {intl.formatMessage({ - id: stepLabelMessageId, - })} - </StepLabel> - <StepContent> - <ErrorBoundryWrapper> + steps.map( + ( + { + StepComponent, + stepLabelMessageId, + state: { error, progress, logsToken }, + }, + index + ) => { + return ( + <Step + key={`PreSave-step-${index}`} + completed={progress >= ProgressFinished} + > + <StepLabel error={Boolean(error)}> + {intl.formatMessage({ + id: stepLabelMessageId, + })} + </StepLabel> + <StepContent> + <ErrorBoundryWrapper> + <LoopIndexContextProvider value={index}> {progress === ProgressStates.RUNNING ? ( <LinearProgress /> ) : null} @@ -71,15 +72,13 @@ function Content() { }} /> - <StepComponent stepIndex={index} /> - </ErrorBoundryWrapper> - </StepContent> - </Step> - ); - } - ) - ) : ( - <LinearProgress /> + <StepComponent /> + </LoopIndexContextProvider> + </ErrorBoundryWrapper> + </StepContent> + </Step> + ); + } ), [intl, steps] ); diff --git a/src/components/shared/Entity/prompts/PreSave/Title.tsx b/src/components/shared/Entity/prompts/PreSave/Title.tsx index cbb441fbd..8a3d9623a 100644 --- a/src/components/shared/Entity/prompts/PreSave/Title.tsx +++ b/src/components/shared/Entity/prompts/PreSave/Title.tsx @@ -7,14 +7,10 @@ function Title() { const intl = useIntl(); const theme = useTheme(); - const [activeStep, setActiveStep, setShow] = usePreSavePromptStore( - (state) => [state.activeStep, state.setActiveStep, state.setShow] - ); - - const closeDialog = () => { - setActiveStep(0); - setShow(false); - }; + const [activeStep, setShow] = usePreSavePromptStore((state) => [ + state.activeStep, + state.setShow, + ]); return ( <DialogTitle @@ -25,7 +21,12 @@ function Title() { }} > Please review your changes - <IconButton disabled={activeStep > 2} onClick={closeDialog}> + <IconButton + disabled={activeStep > 2} + onClick={() => { + setShow(false); + }} + > <Xmark aria-label={intl.formatMessage({ id: 'cta.close' })} style={{ diff --git a/src/components/shared/Entity/prompts/PreSave/index.tsx b/src/components/shared/Entity/prompts/PreSave/index.tsx index a29f71fbb..ccb534c8f 100644 --- a/src/components/shared/Entity/prompts/PreSave/index.tsx +++ b/src/components/shared/Entity/prompts/PreSave/index.tsx @@ -1,17 +1,18 @@ import { Dialog } from '@mui/material'; -import { usePreSavePromptStore } from '../store/usePreSavePromptStore'; +import { + usePreSavePromptStore, + usePreSavePromptStore_onFirstStep, +} from '../store/usePreSavePromptStore'; import Actions from './Actions'; import Content from './Content'; import Title from './Title'; function PreSavePrompt() { - const [activeStep, show] = usePreSavePromptStore((state) => [ - state.activeStep, - state.show, - ]); + const [show] = usePreSavePromptStore((state) => [state.show]); + const onFirstStep = usePreSavePromptStore_onFirstStep(); return ( - <Dialog maxWidth={activeStep === 0 ? 'lg' : 'md'} fullWidth open={show}> + <Dialog maxWidth={onFirstStep ? 'lg' : 'md'} fullWidth open={show}> <Title /> <Content /> <Actions /> diff --git a/src/components/shared/Entity/prompts/steps/dataFlowReset/DisableCapture/definition.ts b/src/components/shared/Entity/prompts/steps/dataFlowReset/DisableCapture/definition.ts index 2509843ed..14431b36f 100644 --- a/src/components/shared/Entity/prompts/steps/dataFlowReset/DisableCapture/definition.ts +++ b/src/components/shared/Entity/prompts/steps/dataFlowReset/DisableCapture/definition.ts @@ -2,13 +2,12 @@ import { defaultStepState } from '../../../store/shared'; import { PromptStep } from '../../../types'; import DisableCapture from '.'; -export const DisableCaptureStep: PromptStep<{ - logsToken: string | null; -}> = { +// interface DisableCaptureStepContext { +// pubId: string | null; +// logsToken: string | null; +// } +export const DisableCaptureStep: PromptStep = { StepComponent: DisableCapture, stepLabelMessageId: 'dataFlowReset.disableCapture.title', state: defaultStepState, - context: { - logsToken: null, - }, }; diff --git a/src/components/shared/Entity/prompts/steps/dataFlowReset/DisableCapture/index.tsx b/src/components/shared/Entity/prompts/steps/dataFlowReset/DisableCapture/index.tsx index 21371f8c0..23db51805 100644 --- a/src/components/shared/Entity/prompts/steps/dataFlowReset/DisableCapture/index.tsx +++ b/src/components/shared/Entity/prompts/steps/dataFlowReset/DisableCapture/index.tsx @@ -5,31 +5,40 @@ import { useEditorStore_queryResponse_draftSpecs, } from 'components/editor/Store/hooks'; import { ProgressStates } from 'components/tables/RowActions/Shared/types'; +import { useLoopIndex } from 'context/LoopIndex/useLoopIndex'; import useJobStatusPoller from 'hooks/useJobStatusPoller'; import { useMount } from 'react-use'; +import { useFormStateStore_setFormState } from 'stores/FormState/hooks'; +import { FormStatus } from 'stores/FormState/types'; import { generateDisabledSpec } from 'utils/entity-utils'; import { usePreSavePromptStore } from '../../../store/usePreSavePromptStore'; -import { StepComponentProps } from '../../../types'; -function DisableCapture({ stepIndex }: StepComponentProps) { +function DisableCapture() { const draftId = useEditorStore_id(); const draftSpecs = useEditorStore_queryResponse_draftSpecs(); const { jobStatusPoller } = useJobStatusPoller(); - const thisStep = usePreSavePromptStore((state) => state.steps?.[stepIndex]); + const stepIndex = useLoopIndex(); + const thisStep = usePreSavePromptStore((state) => state.steps[stepIndex]); - const [updateStep, nextStep] = usePreSavePromptStore((state) => [ - state.updateStep, - state.nextStep, - ]); + const [updateStep, updateContext, nextStep] = usePreSavePromptStore( + (state) => [state.updateStep, state.updateContext, state.nextStep] + ); + + const setFormState = useFormStateStore_setFormState(); useMount(() => { - if (thisStep?.state.progress === ProgressStates.IDLE) { + if (thisStep.state.progress === ProgressStates.IDLE) { + setFormState({ + status: FormStatus.LOCKED, + exitWhenLogsClose: true, + }); + updateStep(stepIndex, { progress: ProgressStates.RUNNING, }); - const foo = async () => { + const disableCaptureAndPublish = async () => { // Update the Capture to be disabled const updateResponse = await modifyDraftSpec( generateDisabledSpec(draftSpecs[0].spec, false, false), @@ -59,7 +68,8 @@ function DisableCapture({ stepIndex }: StepComponentProps) { return; } - updateStep(stepIndex, { + updateContext({ + pubId: publishResponse.data[0].id, logsToken: publishResponse.data[0].logs_token, }); @@ -84,7 +94,9 @@ function DisableCapture({ stepIndex }: StepComponentProps) { ); }; - void foo(); + void disableCaptureAndPublish(); + } else { + console.log('TODO: need to handle showing previous state?'); } }); diff --git a/src/components/shared/Entity/prompts/steps/dataFlowReset/EnableCapture/definition.ts b/src/components/shared/Entity/prompts/steps/dataFlowReset/EnableCapture/definition.ts index aa3055ae9..7df104044 100644 --- a/src/components/shared/Entity/prompts/steps/dataFlowReset/EnableCapture/definition.ts +++ b/src/components/shared/Entity/prompts/steps/dataFlowReset/EnableCapture/definition.ts @@ -2,13 +2,8 @@ import { defaultStepState } from '../../../store/shared'; import { PromptStep } from '../../../types'; import EnableCapture from '.'; -export const EnableCaptureStep: PromptStep<{ - logsToken: string | null; -}> = { +export const EnableCaptureStep: PromptStep = { StepComponent: EnableCapture, stepLabelMessageId: 'dataFlowReset.enableCapture.title', state: defaultStepState, - context: { - logsToken: null, - }, }; diff --git a/src/components/shared/Entity/prompts/steps/dataFlowReset/SelectMaterialization/Materializations.tsx b/src/components/shared/Entity/prompts/steps/dataFlowReset/SelectMaterialization/Materializations.tsx index e3657285d..4ccc16e0a 100644 --- a/src/components/shared/Entity/prompts/steps/dataFlowReset/SelectMaterialization/Materializations.tsx +++ b/src/components/shared/Entity/prompts/steps/dataFlowReset/SelectMaterialization/Materializations.tsx @@ -1,11 +1,9 @@ import { Box, LinearProgress, Typography } from '@mui/material'; import Error from 'components/shared/Error'; import { useLiveSpecsExt_related } from 'hooks/useLiveSpecsExt'; -import { useEffect, useMemo } from 'react'; +import { useMemo } from 'react'; import { useIntl } from 'react-intl'; -import { useBindingStore } from 'stores/Binding/Store'; import { hasLength } from 'utils/misc-utils'; -import { usePreSavePromptStore } from '../../../store/usePreSavePromptStore'; import Selector from './Selector'; import { BindingReviewProps } from './types'; @@ -14,22 +12,8 @@ function Materializations({ selected }: BindingReviewProps) { const { related, error, isValidating } = useLiveSpecsExt_related(selected); - const updateStep = usePreSavePromptStore((state) => state.updateStep); - const foundData = useMemo(() => hasLength(related), [related]); - const [backfillDataFlowTarget] = useBindingStore((state) => [ - state.backfillDataFlowTarget, - ]); - - useEffect(() => { - updateStep(1, { - valid: Boolean( - (!isValidating && !foundData) || backfillDataFlowTarget - ), - }); - }, [backfillDataFlowTarget, foundData, isValidating, updateStep]); - return ( <Box> <Typography> diff --git a/src/components/shared/Entity/prompts/steps/dataFlowReset/SelectMaterialization/Selector.tsx b/src/components/shared/Entity/prompts/steps/dataFlowReset/SelectMaterialization/Selector.tsx index ca6a8d00f..306e13c7a 100644 --- a/src/components/shared/Entity/prompts/steps/dataFlowReset/SelectMaterialization/Selector.tsx +++ b/src/components/shared/Entity/prompts/steps/dataFlowReset/SelectMaterialization/Selector.tsx @@ -2,7 +2,9 @@ import { Autocomplete, Grid, TextField } from '@mui/material'; import { autoCompleteDefaults_Virtual } from 'components/shared/AutoComplete/DefaultProps'; import { ReactNode, useState } from 'react'; import { useIntl } from 'react-intl'; +import { useLoopIndex } from 'context/LoopIndex/useLoopIndex'; import { useBindingStore } from 'stores/Binding/Store'; +import { usePreSavePromptStore } from '../../../store/usePreSavePromptStore'; import { RelatedMaterializationSelectorProps } from './types'; import SelectorOption from './SelectorOption'; @@ -12,6 +14,13 @@ const getValue = (option: any) => function Selector({ disabled, keys }: RelatedMaterializationSelectorProps) { const intl = useIntl(); + const stepIndex = useLoopIndex(); + + const [updateStep, updateContext] = usePreSavePromptStore((state) => [ + state.updateStep, + state.updateContext, + ]); + const [inputValue, setInputValue] = useState(''); const [backfillDataFlowTarget, setBackfillDataFlowTarget] = useBindingStore( @@ -21,8 +30,6 @@ function Selector({ disabled, keys }: RelatedMaterializationSelectorProps) { ] ); - console.log('keys', keys); - if (keys.length === 0) { return null; } @@ -40,9 +47,20 @@ function Selector({ disabled, keys }: RelatedMaterializationSelectorProps) { options={keys} value={backfillDataFlowTarget} onChange={(_event, newValue) => { - setBackfillDataFlowTarget( - newValue ? newValue.catalog_name : null - ); + const backfillTarget = newValue + ? newValue.catalog_name + : null; + + // Need to save the data somewhere better + setBackfillDataFlowTarget(backfillTarget); + + updateStep(stepIndex, { + valid: Boolean(backfillTarget), + }); + + updateContext({ + backfillTarget, + }); }} onInputChange={(_event, newInputValue) => { setInputValue(newInputValue); diff --git a/src/components/shared/Entity/prompts/steps/dataFlowReset/SelectMaterialization/definition.ts b/src/components/shared/Entity/prompts/steps/dataFlowReset/SelectMaterialization/definition.ts index 76abe504e..3cf04e25d 100644 --- a/src/components/shared/Entity/prompts/steps/dataFlowReset/SelectMaterialization/definition.ts +++ b/src/components/shared/Entity/prompts/steps/dataFlowReset/SelectMaterialization/definition.ts @@ -2,16 +2,15 @@ import { defaultStepState } from '../../../store/shared'; import { PromptStep } from '../../../types'; import SelectMaterialization from '.'; -export const SelectMaterializationStep: PromptStep<{ - backfillTarget: string | null; -}> = { +// interface SelectMaterializationStepContext { +// backfillTarget: string | null; +// } + +export const SelectMaterializationStep: PromptStep = { StepComponent: SelectMaterialization, stepLabelMessageId: 'dataFlowReset.selectMaterialization.title', state: { ...defaultStepState, valid: false, }, - context: { - backfillTarget: null, - }, }; diff --git a/src/components/shared/Entity/prompts/steps/dataFlowReset/UpdateMaterialization/definition.ts b/src/components/shared/Entity/prompts/steps/dataFlowReset/UpdateMaterialization/definition.ts index 968fd5c75..0b8046b96 100644 --- a/src/components/shared/Entity/prompts/steps/dataFlowReset/UpdateMaterialization/definition.ts +++ b/src/components/shared/Entity/prompts/steps/dataFlowReset/UpdateMaterialization/definition.ts @@ -2,11 +2,14 @@ import { defaultStepState } from '../../../store/shared'; import { PromptStep } from '../../../types'; import UpdateMaterialization from '.'; -export const UpdateMaterializationStep: PromptStep<null> = { +// interface UpdateMaterializationStepContext { +// backfilledDraftId: string | null; +// } + +export const UpdateMaterializationStep: PromptStep = { StepComponent: UpdateMaterialization, stepLabelMessageId: 'dataFlowReset.updateMaterialization.title', state: { ...defaultStepState, }, - context: null, }; diff --git a/src/components/shared/Entity/prompts/steps/dataFlowReset/UpdateMaterialization/index.tsx b/src/components/shared/Entity/prompts/steps/dataFlowReset/UpdateMaterialization/index.tsx index 81122112b..d5395bcbd 100644 --- a/src/components/shared/Entity/prompts/steps/dataFlowReset/UpdateMaterialization/index.tsx +++ b/src/components/shared/Entity/prompts/steps/dataFlowReset/UpdateMaterialization/index.tsx @@ -1,5 +1,101 @@ +import { createEntityDraft } from 'api/drafts'; +import { createDraftSpec } from 'api/draftSpecs'; +import { getLiveSpecsByLiveSpecId } from 'api/liveSpecsExt'; + +import { ProgressStates } from 'components/tables/RowActions/Shared/types'; +import { useLoopIndex } from 'context/LoopIndex/useLoopIndex'; +import { useMount } from 'react-use'; +import { usePreSavePromptStore } from '../../../store/usePreSavePromptStore'; + function MarkMaterialization() { - return <>describe what we're doing</>; + const stepIndex = useLoopIndex(); + const thisStep = usePreSavePromptStore((state) => state.steps[stepIndex]); + + const [updateStep, updateContext, nextStep, context] = + usePreSavePromptStore((state) => [ + state.updateStep, + state.updateContext, + state.nextStep, + state.context, + ]); + + useMount(() => { + if (thisStep.state.progress === ProgressStates.IDLE) { + updateStep(stepIndex, { + progress: ProgressStates.RUNNING, + }); + + const updateMaterializationTimestamp = async () => { + const liveSpecResponse = await getLiveSpecsByLiveSpecId( + context.liveSpecId + ); + + const catalogName = liveSpecResponse.data?.[0].catalog_name; + + if (liveSpecResponse.error || !catalogName) { + updateStep(stepIndex, { + error: liveSpecResponse.error, + progress: ProgressStates.FAILED, + }); + return; + } + + const draftsResponse = await createEntityDraft(catalogName); + const backfilledDraftId = draftsResponse.data?.[0].id; + + if (draftsResponse.error || !backfilledDraftId) { + updateStep(stepIndex, { + error: draftsResponse.error, + progress: ProgressStates.FAILED, + }); + return; + } + + updateContext({ + backfilledDraftId, + }); + + // Update the spec here + const updatedSpec = { + ...liveSpecResponse.data?.[0].spec, + }; + + const draftedMaterialization = await createDraftSpec( + backfilledDraftId, + catalogName, + updatedSpec, + 'materialization' + ); + + if (draftedMaterialization.error) { + updateStep(stepIndex, { + error: draftedMaterialization.error, + progress: ProgressStates.FAILED, + }); + return; + } + + nextStep(); + }; + + void updateMaterializationTimestamp(); + } else { + console.log('TODO: need to handle showing previous state?'); + } + }); + + return ( + <>explain what is happening</> + // <Logs + // token={logsToken} + // height={350} + // loadingLineSeverity="info" + // spinnerMessages={{ + // stoppedKey: 'preSavePrompt.logs.spinner.stopped', + // runningKey: 'preSavePrompt.logs.spinner.running', + // }} + // /> + ); } export default MarkMaterialization; diff --git a/src/components/shared/Entity/prompts/steps/dataFlowReset/WaitForCaptureStop/definition.ts b/src/components/shared/Entity/prompts/steps/dataFlowReset/WaitForCaptureStop/definition.ts index 2102f39a5..177e0a8dd 100644 --- a/src/components/shared/Entity/prompts/steps/dataFlowReset/WaitForCaptureStop/definition.ts +++ b/src/components/shared/Entity/prompts/steps/dataFlowReset/WaitForCaptureStop/definition.ts @@ -2,11 +2,15 @@ import { defaultStepState } from '../../../store/shared'; import { PromptStep } from '../../../types'; import WaitForCapture from '.'; -export const WaitForCaptureStep: PromptStep<null> = { +// interface WaitForCaptureStepContext { +// liveSpecId: string | null; +// timeStopped: string | null; +// } + +export const WaitForCaptureStep: PromptStep = { StepComponent: WaitForCapture, stepLabelMessageId: 'dataFlowReset.waitForCapture.title', state: { ...defaultStepState, }, - context: null, }; diff --git a/src/components/shared/Entity/prompts/steps/dataFlowReset/WaitForCaptureStop/index.tsx b/src/components/shared/Entity/prompts/steps/dataFlowReset/WaitForCaptureStop/index.tsx index 0783e89f6..c274e0fd5 100644 --- a/src/components/shared/Entity/prompts/steps/dataFlowReset/WaitForCaptureStop/index.tsx +++ b/src/components/shared/Entity/prompts/steps/dataFlowReset/WaitForCaptureStop/index.tsx @@ -1,5 +1,74 @@ +import { getLiveSpecIdByPublication } from 'api/publicationSpecsExt'; +import { useEditorStore_catalogName } from 'components/editor/Store/hooks'; +import { ProgressStates } from 'components/tables/RowActions/Shared/types'; +import { useLoopIndex } from 'context/LoopIndex/useLoopIndex'; +import { useMount } from 'react-use'; +import { usePreSavePromptStore } from '../../../store/usePreSavePromptStore'; + function WaitForCaptureStop() { - return <>No logs... but show something</>; + const catalogName = useEditorStore_catalogName(); + + const stepIndex = useLoopIndex(); + const thisStep = usePreSavePromptStore((state) => state.steps[stepIndex]); + + const [context, updateStep, updateContext, nextStep] = + usePreSavePromptStore((state) => [ + state.context, + state.updateStep, + state.updateContext, + state.nextStep, + ]); + + useMount(() => { + if (thisStep.state.progress === ProgressStates.IDLE) { + updateStep(stepIndex, { + progress: ProgressStates.RUNNING, + }); + + const waitForSpecToFullyStop = async () => { + const liveSpecResponse = await getLiveSpecIdByPublication( + context.pubId, + catalogName + ); + + const liveSpecId = liveSpecResponse.data?.[0].live_spec_id; + + if (liveSpecResponse.error || !liveSpecId) { + updateStep(stepIndex, { + error: liveSpecResponse.error, + progress: ProgressStates.FAILED, + }); + return; + } + + // TODO (data flow) this needs to actually fetch the time for this + // Start calling for shards + // Loop over it until we see nothing is coming + // Snag time + updateContext({ + liveSpecId, + timeStopped: '01/01/2024', + }); + + updateStep(stepIndex, { + valid: true, + }); + + // Fake timeout to make it feel more async + setTimeout(() => { + nextStep(); + }, 1000); + }; + + void waitForSpecToFullyStop(); + } else { + console.log('TODO: need to handle showing previous state?'); + } + }); + + return ( + <>explain what is happening context.liveSpecId = {context.liveSpecId}</> + ); } export default WaitForCaptureStop; diff --git a/src/components/shared/Entity/prompts/steps/preSave/ChangeReview/definition.ts b/src/components/shared/Entity/prompts/steps/preSave/ChangeReview/definition.ts index 84fcb32f5..7586c885e 100644 --- a/src/components/shared/Entity/prompts/steps/preSave/ChangeReview/definition.ts +++ b/src/components/shared/Entity/prompts/steps/preSave/ChangeReview/definition.ts @@ -2,12 +2,11 @@ import { defaultStepState } from '../../../store/shared'; import { PromptStep } from '../../../types'; import ChangeReview from '.'; -export const ChangeReviewStep: PromptStep<null> = { +export const ChangeReviewStep: PromptStep = { StepComponent: ChangeReview, stepLabelMessageId: 'preSavePrompt.changeReview.title', state: { ...defaultStepState, valid: true, }, - context: null, }; diff --git a/src/components/shared/Entity/prompts/steps/preSave/Publish/definition.ts b/src/components/shared/Entity/prompts/steps/preSave/Publish/definition.ts index 7c6a0a92e..5b0f4f9fa 100644 --- a/src/components/shared/Entity/prompts/steps/preSave/Publish/definition.ts +++ b/src/components/shared/Entity/prompts/steps/preSave/Publish/definition.ts @@ -2,9 +2,8 @@ import { defaultStepState } from '../../../store/shared'; import { PromptStep } from '../../../types'; import Publish from '.'; -export const PublishStep: PromptStep<null> = { +export const PublishStep: PromptStep = { StepComponent: Publish, stepLabelMessageId: 'preSavePrompt.publish.title', state: defaultStepState, - context: null, }; diff --git a/src/components/shared/Entity/prompts/store/Hydrator.tsx b/src/components/shared/Entity/prompts/store/Hydrator.tsx index 9e57831c4..49f86d367 100644 --- a/src/components/shared/Entity/prompts/store/Hydrator.tsx +++ b/src/components/shared/Entity/prompts/store/Hydrator.tsx @@ -1,12 +1,13 @@ import { BaseComponentProps } from 'types'; -import { useEffect } from 'react'; +import { useEffect, useRef } from 'react'; import { useBinding_backfilledBindings_count } from 'stores/Binding/hooks'; import { useBindingStore } from 'stores/Binding/Store'; import { usePreSavePromptStore } from './usePreSavePromptStore'; function PromptsHydrator({ children }: BaseComponentProps) { - const [resetState, initializeSteps] = usePreSavePromptStore((state) => [ - state.resetState, + const initialized = useRef(false); + + const [initializeSteps] = usePreSavePromptStore((state) => [ state.initializeSteps, ]); @@ -14,12 +15,14 @@ function PromptsHydrator({ children }: BaseComponentProps) { const needsBackfilled = useBinding_backfilledBindings_count(); useEffect(() => { - initializeSteps(Boolean(backfillDataflow && needsBackfilled)); + if (initialized.current) { + console.log('skipping hydration - this will probably break'); + return; + } - return () => { - resetState(); - }; - }, [backfillDataflow, initializeSteps, needsBackfilled, resetState]); + initializeSteps(Boolean(backfillDataflow && needsBackfilled)); + initialized.current = true; + }, [backfillDataflow, initializeSteps, needsBackfilled]); // eslint-disable-next-line react/jsx-no-useless-fragment return <>{children}</>; diff --git a/src/components/shared/Entity/prompts/store/types.ts b/src/components/shared/Entity/prompts/store/types.ts new file mode 100644 index 000000000..23e25f772 --- /dev/null +++ b/src/components/shared/Entity/prompts/store/types.ts @@ -0,0 +1,23 @@ +import { PromptStep, PromptStepState } from '../types'; + +// TODO (typing) would like to auto generate the `context` type somehow +// by combining multiple steps contexts +export interface PreSavePromptStore<T = any> { + steps: PromptStep[]; + + context: T; + updateContext: (setting: any) => void; + + updateStep: (step: number, settings: Partial<PromptStepState>) => void; + initializeSteps: (backfillEnabled: boolean) => void; + + activeStep: number; + setActiveStep: (val: PreSavePromptStore['activeStep']) => void; + nextStep: () => void; + previousStep: () => void; + + show: boolean; + setShow: (data: PreSavePromptStore['show']) => void; + + resetState: () => void; +} diff --git a/src/components/shared/Entity/prompts/store/usePreSavePromptStore.ts b/src/components/shared/Entity/prompts/store/usePreSavePromptStore.ts index 2b9da7a0d..f1b80b590 100644 --- a/src/components/shared/Entity/prompts/store/usePreSavePromptStore.ts +++ b/src/components/shared/Entity/prompts/store/usePreSavePromptStore.ts @@ -4,52 +4,27 @@ import { devtools } from 'zustand/middleware'; import { devtoolsOptions } from 'utils/store-utils'; import { useShallow } from 'zustand/react/shallow'; import { ProgressStates } from 'components/tables/RowActions/Shared/types'; -import { PromptStep, PromptStepState } from '../types'; -import { - DataFlowResetSteps, - DataFlowSteps, -} from '../steps/dataFlowReset/shared'; +import { PromptStep } from '../types'; +import { DataFlowResetSteps } from '../steps/dataFlowReset/shared'; import { ChangeReviewStep } from '../steps/preSave/ChangeReview/definition'; import { PublishStep } from '../steps/preSave/Publish/definition'; - -interface PreSavePromptStore { - steps: PromptStep[] | null; - machine: any; - updateMachine: (key: string, settings: Partial<PromptStep>) => void; - - updateStep: (step: number, settings: Partial<PromptStepState>) => void; - initializeSteps: (backfillEnabled: boolean) => void; - - activeStep: number; - activeMachine: string; - setActiveStep: (val: PreSavePromptStore['activeStep']) => void; - nextStep: () => void; - previousStep: () => void; - - show: boolean; - setShow: (data: PreSavePromptStore['show']) => void; - - resetState: () => void; -} +import { PreSavePromptStore } from './types'; const getInitialState = (): Pick< PreSavePromptStore, - 'activeStep' | 'activeMachine' | 'steps' | 'show' | 'machine' + 'activeStep' | 'steps' | 'show' | 'context' > => ({ activeStep: 0, - activeMachine: 'changeReview', show: false, - steps: null, - machine: {}, + steps: [], + context: {}, }); -const name = 'estuary.presave-prompt-store'; - export const usePreSavePromptStore = create<PreSavePromptStore>()( devtools((set) => { return { ...getInitialState(), - resetState: () => set(getInitialState(), false, 'state reset'), + resetState: () => set(getInitialState(), false, 'resetState'), initializeSteps: (backfillEnabled) => set( @@ -61,20 +36,8 @@ export const usePreSavePromptStore = create<PreSavePromptStore>()( } newSteps.push(PublishStep); - state.steps = newSteps; - if (backfillEnabled) { - state.machine = { - changeReview: ChangeReviewStep, - ...DataFlowSteps, - saveAndPublish: PublishStep, - }; - } else { - state.machine = { - changeReview: ChangeReviewStep, - saveAndPublish: PublishStep, - }; - } + state.steps = newSteps; }), false, 'initializeSteps' @@ -83,10 +46,6 @@ export const usePreSavePromptStore = create<PreSavePromptStore>()( updateStep: (stepToUpdate, settings) => set( produce((state: PreSavePromptStore) => { - if (!state.steps) { - return; - } - state.steps[stepToUpdate].state = { ...state.steps[stepToUpdate].state, ...settings, @@ -96,20 +55,16 @@ export const usePreSavePromptStore = create<PreSavePromptStore>()( 'setActiveStep' ), - updateMachine: (machineToUpdate, settings) => + updateContext: (settings) => set( produce((state: PreSavePromptStore) => { - if (!state.machine) { - return; - } - - state.machine[machineToUpdate] = { - ...state.machine[machineToUpdate], + state.context = { + ...state.context, ...settings, }; }), false, - 'updateMachine' + 'updateContext' ), setActiveStep: (value) => @@ -124,10 +79,6 @@ export const usePreSavePromptStore = create<PreSavePromptStore>()( nextStep: () => set( produce((state: PreSavePromptStore) => { - if (!state.steps) { - return; - } - if (state.steps[state.activeStep].state.valid) { state.steps[state.activeStep].state.progress = ProgressStates.SUCCESS; @@ -141,10 +92,16 @@ export const usePreSavePromptStore = create<PreSavePromptStore>()( previousStep: () => set( produce((state: PreSavePromptStore) => { - state.activeStep = state.activeStep - 1; + const newVal = state.activeStep - 1; + state.activeStep = newVal >= 0 ? newVal : 0; + + // TODO - why did this not work? + // if (state.activeStep === 0) { + // state.setShow(false); + // } }), false, - 'nextStep' + 'previousStep' ), setShow: (val) => @@ -160,17 +117,23 @@ export const usePreSavePromptStore = create<PreSavePromptStore>()( 'setShow' ), }; - }, devtoolsOptions(name)) + }, devtoolsOptions('estuary.presave-prompt-store')) ); export const usePreSavePromptStore_activeStep = () => { return usePreSavePromptStore( useShallow((state) => { - return state.steps?.[state.activeStep]?.state; + return state.steps[state.activeStep]?.state; }) ); }; +export const usePreSavePromptStore_stepValid = () => { + return usePreSavePromptStore( + useShallow((state) => state.steps[state.activeStep].state.valid) + ); +}; + export const usePreSavePromptStore_onFirstStep = () => { return usePreSavePromptStore( useShallow((state) => { diff --git a/src/components/shared/Entity/prompts/types.ts b/src/components/shared/Entity/prompts/types.ts index 8f2cb1c4f..d99ac9795 100644 --- a/src/components/shared/Entity/prompts/types.ts +++ b/src/components/shared/Entity/prompts/types.ts @@ -19,13 +19,9 @@ export interface PromptStepState { logsToken?: string; } -export interface StepComponentProps { - stepIndex: number; -} - -export interface PromptStep<T = any> { - StepComponent: (props: StepComponentProps) => EmotionJSX.Element; +// TODO (dataflow typing) should try to get typing working with the context +export interface PromptStep { + StepComponent: () => EmotionJSX.Element; stepLabelMessageId: string; state: PromptStepState; - context: T; } diff --git a/src/context/LoopIndex/index.tsx b/src/context/LoopIndex/index.tsx new file mode 100644 index 000000000..c1b3676b3 --- /dev/null +++ b/src/context/LoopIndex/index.tsx @@ -0,0 +1,13 @@ +import { LoopIndexContext } from './shared'; +import { LoopIndexContextProps } from './types'; + +export const LoopIndexContextProvider = ({ + children, + value, +}: LoopIndexContextProps) => { + return ( + <LoopIndexContext.Provider value={value}> + {children} + </LoopIndexContext.Provider> + ); +}; diff --git a/src/context/LoopIndex/shared.ts b/src/context/LoopIndex/shared.ts new file mode 100644 index 000000000..421ff6818 --- /dev/null +++ b/src/context/LoopIndex/shared.ts @@ -0,0 +1,3 @@ +import { createContext } from 'react'; + +export const LoopIndexContext = createContext<number | null>(null); diff --git a/src/context/LoopIndex/types.ts b/src/context/LoopIndex/types.ts new file mode 100644 index 000000000..cc81167d2 --- /dev/null +++ b/src/context/LoopIndex/types.ts @@ -0,0 +1,5 @@ +import { BaseComponentProps } from 'types'; + +export interface LoopIndexContextProps extends BaseComponentProps { + value: number; +} diff --git a/src/context/LoopIndex/useLoopIndex.tsx b/src/context/LoopIndex/useLoopIndex.tsx new file mode 100644 index 000000000..a9f3d6a77 --- /dev/null +++ b/src/context/LoopIndex/useLoopIndex.tsx @@ -0,0 +1,14 @@ +import { useContext } from 'react'; +import { LoopIndexContext } from './shared'; + +export const useLoopIndex = () => { + const context = useContext(LoopIndexContext); + + if (context === null) { + throw new Error( + 'useLoopIndex must be used within a LoopIndexContextProvider' + ); + } + + return context; +}; diff --git a/src/stores/FormState/Store.ts b/src/stores/FormState/Store.ts index d04027253..903164d03 100644 --- a/src/stores/FormState/Store.ts +++ b/src/stores/FormState/Store.ts @@ -2,6 +2,7 @@ import produce from 'immer'; import { logRocketConsole } from 'services/shared'; import { CustomEvents } from 'services/types'; import { MessagePrefixes } from 'types'; +import { hasLength } from 'utils/misc-utils'; import { devtoolsOptions } from 'utils/store-utils'; import { create, StoreApi } from 'zustand'; import { devtools, NamedSet } from 'zustand/middleware'; @@ -17,7 +18,10 @@ const formActive = (status: FormStatus) => { status === FormStatus.UPDATING || // TODO (workflow stores) need to manage form state better // This is crappy - sorry. But if we have saved we want to disable everything and this is quickest way - status === FormStatus.SAVED + status === FormStatus.SAVED || + // This is like 'saved' but a bit different. With PreSavePrompt we need a way to make sure the user + // never is able to get back out of that ever + status === FormStatus.LOCKED ); }; @@ -134,6 +138,19 @@ const getInitialState = ( return; } + if ( + formState.status === FormStatus.LOCKED && + hasLength(newState.status) + ) { + // If we are here this means somehow the user is trying to take an action + // AFTER we have locked it and that should not happen ever. It does not matter + // what state it wants to go do - after being 'locked' it cannot go back. + logRocketConsole(CustomEvents.FORM_STATE_PREVENTED, { + type: 'locked', + }); + return; + } + state.formState = { ...formState, ...newState }; state.isIdle = formIdle(state.formState.status); diff --git a/src/stores/FormState/types.ts b/src/stores/FormState/types.ts index f3871c88f..3738ae3ac 100644 --- a/src/stores/FormState/types.ts +++ b/src/stores/FormState/types.ts @@ -39,6 +39,9 @@ export enum FormStatus { UPDATED = 'UPDATED', FAILED = 'FAILED', + + // USE WITH CAUTION - only for prompts right now (Q3 2024) + LOCKED = 'LOCKED', } export interface EntityFormState { From 0e329e99af9a77047f0c99203d7f44ed8a042c8d Mon Sep 17 00:00:00 2001 From: Travis Jenkins <travis@estuary.dev> Date: Wed, 18 Sep 2024 17:08:51 -0400 Subject: [PATCH 16/66] Adding handled to get no response when an insert is done to reduce network Fetching live spec based on spec id --- src/api/draftSpecs.ts | 5 +++-- src/api/liveSpecsExt.ts | 9 +++++++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/api/draftSpecs.ts b/src/api/draftSpecs.ts index faa348b98..1c7d38bd6 100644 --- a/src/api/draftSpecs.ts +++ b/src/api/draftSpecs.ts @@ -42,7 +42,8 @@ export const createDraftSpec = ( catalogName: string, draftSpec: any, specType?: Entity | null, - lastPubId?: string | null + lastPubId?: string | null, + noResponse?: boolean ) => { let matchData: CreateMatchData = { draft_id: draftId, @@ -55,7 +56,7 @@ export const createDraftSpec = ( matchData = { ...matchData, expect_pub_id: lastPubId }; } - return insertSupabase(TABLES.DRAFT_SPECS, matchData); + return insertSupabase(TABLES.DRAFT_SPECS, matchData, noResponse); }; export const modifyDraftSpec = ( diff --git a/src/api/liveSpecsExt.ts b/src/api/liveSpecsExt.ts index e000d9a85..5c7aeacd8 100644 --- a/src/api/liveSpecsExt.ts +++ b/src/api/liveSpecsExt.ts @@ -369,6 +369,14 @@ const getLiveSpecsByLiveSpecId = async (liveSpecId: string) => { return data; }; +const getLiveSpecSpec = (liveSpecId: string) => { + return supabaseClient + .from(TABLES.LIVE_SPECS_EXT) + .select(`spec`) + .eq('id', liveSpecId) + .single(); +}; + const getLiveSpecShards = (tenant: string, entityType: Entity) => { return supabaseClient .from(TABLES.LIVE_SPECS_EXT) @@ -389,4 +397,5 @@ export { getLiveSpecsByConnectorId, getLiveSpecsByLiveSpecId, getLiveSpecShards, + getLiveSpecSpec, }; From 8cacc5924474cf47c7f956500db282208456d977 Mon Sep 17 00:00:00 2001 From: Travis Jenkins <travis@estuary.dev> Date: Wed, 18 Sep 2024 17:11:51 -0400 Subject: [PATCH 17/66] Storing stuff to re-enable the capture Wiring up most of the steps (without waiting for no data) --- .../shared/Entity/prompts/PreSave/Actions.tsx | 2 +- .../DisableCapture/definition.ts | 2 + .../dataFlowReset/DisableCapture/index.tsx | 19 ++++- .../dataFlowReset/EnableCapture/index.tsx | 77 ++++++++++++++++++- .../SelectMaterialization/BindingReview.tsx | 13 +--- .../SelectMaterialization/Selector.tsx | 33 ++++---- .../SelectMaterialization/definition.ts | 2 +- .../UpdateMaterialization/index.tsx | 52 +++++++++---- .../WaitForCaptureStop/index.tsx | 7 +- .../shared/Entity/prompts/store/Hydrator.tsx | 10 +-- src/services/supabase.ts | 21 ++++- src/stores/Binding/hooks.ts | 10 +++ src/utils/workflow-utils.ts | 11 +++ 13 files changed, 196 insertions(+), 63 deletions(-) diff --git a/src/components/shared/Entity/prompts/PreSave/Actions.tsx b/src/components/shared/Entity/prompts/PreSave/Actions.tsx index 6b7226977..0b7c82591 100644 --- a/src/components/shared/Entity/prompts/PreSave/Actions.tsx +++ b/src/components/shared/Entity/prompts/PreSave/Actions.tsx @@ -18,7 +18,7 @@ function Actions() { ] ); - const canContinue = usePreSavePromptStore_stepValid; + const canContinue = usePreSavePromptStore_stepValid(); const onFirstStep = usePreSavePromptStore_onFirstStep(); return ( diff --git a/src/components/shared/Entity/prompts/steps/dataFlowReset/DisableCapture/definition.ts b/src/components/shared/Entity/prompts/steps/dataFlowReset/DisableCapture/definition.ts index 14431b36f..057a17696 100644 --- a/src/components/shared/Entity/prompts/steps/dataFlowReset/DisableCapture/definition.ts +++ b/src/components/shared/Entity/prompts/steps/dataFlowReset/DisableCapture/definition.ts @@ -4,6 +4,8 @@ import DisableCapture from '.'; // interface DisableCaptureStepContext { // pubId: string | null; +// captureSpec: JSON | null; +// captureName: string | null; // logsToken: string | null; // } export const DisableCaptureStep: PromptStep = { diff --git a/src/components/shared/Entity/prompts/steps/dataFlowReset/DisableCapture/index.tsx b/src/components/shared/Entity/prompts/steps/dataFlowReset/DisableCapture/index.tsx index 23db51805..36caeffaa 100644 --- a/src/components/shared/Entity/prompts/steps/dataFlowReset/DisableCapture/index.tsx +++ b/src/components/shared/Entity/prompts/steps/dataFlowReset/DisableCapture/index.tsx @@ -39,14 +39,25 @@ function DisableCapture() { }); const disableCaptureAndPublish = async () => { + const newSpec = generateDisabledSpec( + draftSpecs[0].spec, + false, + false + ); + + const captureName = draftSpecs[0].catalog_name; + // Update the Capture to be disabled const updateResponse = await modifyDraftSpec( - generateDisabledSpec(draftSpecs[0].spec, false, false), + newSpec, { draft_id: draftId, - catalog_name: draftSpecs[0].catalog_name, + catalog_name: captureName, spec_type: 'capture', - } + }, + undefined, + undefined, + `data flow backfill : ${captureName} : disable : someKeyGoesHere` ); if (updateResponse.error) { @@ -69,6 +80,8 @@ function DisableCapture() { } updateContext({ + captureName, + captureSpec: newSpec, pubId: publishResponse.data[0].id, logsToken: publishResponse.data[0].logs_token, }); diff --git a/src/components/shared/Entity/prompts/steps/dataFlowReset/EnableCapture/index.tsx b/src/components/shared/Entity/prompts/steps/dataFlowReset/EnableCapture/index.tsx index 7cea411f6..6ad20e001 100644 --- a/src/components/shared/Entity/prompts/steps/dataFlowReset/EnableCapture/index.tsx +++ b/src/components/shared/Entity/prompts/steps/dataFlowReset/EnableCapture/index.tsx @@ -1,5 +1,80 @@ +import { modifyDraftSpec } from 'api/draftSpecs'; + +import { ProgressStates } from 'components/tables/RowActions/Shared/types'; +import { useLoopIndex } from 'context/LoopIndex/useLoopIndex'; +import { useMount } from 'react-use'; +import { generateDisabledSpec } from 'utils/entity-utils'; +import { usePreSavePromptStore } from '../../../store/usePreSavePromptStore'; + function EnableCapture() { - return <>Logs</>; + const stepIndex = useLoopIndex(); + const thisStep = usePreSavePromptStore((state) => state.steps[stepIndex]); + + const [updateStep, updateContext, nextStep, context] = + usePreSavePromptStore((state) => [ + state.updateStep, + state.updateContext, + state.nextStep, + state.context, + ]); + + useMount(() => { + if (thisStep.state.progress === ProgressStates.IDLE) { + updateStep(stepIndex, { + progress: ProgressStates.RUNNING, + }); + + const enableCapture = async () => { + // Update the Capture to be disabled + const updateResponse = await modifyDraftSpec( + generateDisabledSpec(context.captureSpec, true, false), + { + draft_id: context.backfilledDraftId, + catalog_name: context.captureName, + spec_type: 'capture', + }, + undefined, + undefined, + `data flow backfill : ${context.captureName} : enable : someKeyGoesHere` + ); + + if (updateResponse.error) { + updateStep(stepIndex, { + error: updateResponse.error, + progress: ProgressStates.FAILED, + }); + return; + } + + console.log('hey we are ready to publish', updateContext); + + updateStep(stepIndex, { + progress: ProgressStates.SUCCESS, + }); + nextStep(); + + // // Start publishing it + // This needs to be the next step + }; + + void enableCapture(); + } else { + console.log('TODO: need to handle showing previous state?'); + } + }); + + return ( + <>explain what is happening</> + // <Logs + // token={logsToken} + // height={350} + // loadingLineSeverity="info" + // spinnerMessages={{ + // stoppedKey: 'preSavePrompt.logs.spinner.stopped', + // runningKey: 'preSavePrompt.logs.spinner.running', + // }} + // /> + ); } export default EnableCapture; diff --git a/src/components/shared/Entity/prompts/steps/dataFlowReset/SelectMaterialization/BindingReview.tsx b/src/components/shared/Entity/prompts/steps/dataFlowReset/SelectMaterialization/BindingReview.tsx index 36825dda8..85c7376cd 100644 --- a/src/components/shared/Entity/prompts/steps/dataFlowReset/SelectMaterialization/BindingReview.tsx +++ b/src/components/shared/Entity/prompts/steps/dataFlowReset/SelectMaterialization/BindingReview.tsx @@ -2,21 +2,14 @@ import { Stack, Typography } from '@mui/material'; import AlertBox from 'components/shared/AlertBox'; import ChipList from 'components/shared/ChipList'; import { useIntl } from 'react-intl'; -import { useBindingStore } from 'stores/Binding/Store'; -import { useShallow } from 'zustand/react/shallow'; +import { useBinding_collectionsBeingBackfilled } from 'stores/Binding/hooks'; import Materializations from './Materializations'; function BindingReview() { const intl = useIntl(); - const collectionsBeingBackfilled = useBindingStore( - useShallow((state) => { - return state.backfilledBindings.map((backfilledBinding) => { - return state.resourceConfigs[backfilledBinding].meta - .collectionName; - }); - }) - ); + const collectionsBeingBackfilled = useBinding_collectionsBeingBackfilled(); + return ( <Stack direction="column" spacing={2}> <AlertBox diff --git a/src/components/shared/Entity/prompts/steps/dataFlowReset/SelectMaterialization/Selector.tsx b/src/components/shared/Entity/prompts/steps/dataFlowReset/SelectMaterialization/Selector.tsx index 306e13c7a..2afec5c15 100644 --- a/src/components/shared/Entity/prompts/steps/dataFlowReset/SelectMaterialization/Selector.tsx +++ b/src/components/shared/Entity/prompts/steps/dataFlowReset/SelectMaterialization/Selector.tsx @@ -3,7 +3,8 @@ import { autoCompleteDefaults_Virtual } from 'components/shared/AutoComplete/Def import { ReactNode, useState } from 'react'; import { useIntl } from 'react-intl'; import { useLoopIndex } from 'context/LoopIndex/useLoopIndex'; -import { useBindingStore } from 'stores/Binding/Store'; +import { useShallow } from 'zustand/react/shallow'; +import { LiveSpecsExt_Related } from 'hooks/useLiveSpecsExt'; import { usePreSavePromptStore } from '../../../store/usePreSavePromptStore'; import { RelatedMaterializationSelectorProps } from './types'; import SelectorOption from './SelectorOption'; @@ -21,15 +22,14 @@ function Selector({ disabled, keys }: RelatedMaterializationSelectorProps) { state.updateContext, ]); - const [inputValue, setInputValue] = useState(''); - - const [backfillDataFlowTarget, setBackfillDataFlowTarget] = useBindingStore( - (state) => [ - state.backfillDataFlowTarget, - state.setBackfillDataFlowTarget, - ] + const backfillTarget = usePreSavePromptStore( + useShallow((state) => { + state.context.backfillTarget; + }) ); + const [inputValue, setInputValue] = useState(''); + if (keys.length === 0) { return null; } @@ -42,24 +42,19 @@ function Selector({ disabled, keys }: RelatedMaterializationSelectorProps) { getOptionLabel={getValue} inputValue={inputValue} isOptionEqualToValue={(option, optionValue) => { - return option.catalog_name === optionValue; + return option.catalog_name === optionValue.catalog_name; }} options={keys} - value={backfillDataFlowTarget} - onChange={(_event, newValue) => { - const backfillTarget = newValue - ? newValue.catalog_name - : null; - - // Need to save the data somewhere better - setBackfillDataFlowTarget(backfillTarget); + value={backfillTarget} + onChange={(_event, newValue: LiveSpecsExt_Related | null) => { + const newBackfillTarget = newValue ? newValue : null; updateStep(stepIndex, { - valid: Boolean(backfillTarget), + valid: Boolean(newBackfillTarget), }); updateContext({ - backfillTarget, + backfillTarget: newBackfillTarget, }); }} onInputChange={(_event, newInputValue) => { diff --git a/src/components/shared/Entity/prompts/steps/dataFlowReset/SelectMaterialization/definition.ts b/src/components/shared/Entity/prompts/steps/dataFlowReset/SelectMaterialization/definition.ts index 3cf04e25d..4c4704fe2 100644 --- a/src/components/shared/Entity/prompts/steps/dataFlowReset/SelectMaterialization/definition.ts +++ b/src/components/shared/Entity/prompts/steps/dataFlowReset/SelectMaterialization/definition.ts @@ -3,7 +3,7 @@ import { PromptStep } from '../../../types'; import SelectMaterialization from '.'; // interface SelectMaterializationStepContext { -// backfillTarget: string | null; +// backfillTarget: LiveSpecsExt_Related | null; // } export const SelectMaterializationStep: PromptStep = { diff --git a/src/components/shared/Entity/prompts/steps/dataFlowReset/UpdateMaterialization/index.tsx b/src/components/shared/Entity/prompts/steps/dataFlowReset/UpdateMaterialization/index.tsx index d5395bcbd..cff31faad 100644 --- a/src/components/shared/Entity/prompts/steps/dataFlowReset/UpdateMaterialization/index.tsx +++ b/src/components/shared/Entity/prompts/steps/dataFlowReset/UpdateMaterialization/index.tsx @@ -1,16 +1,23 @@ import { createEntityDraft } from 'api/drafts'; -import { createDraftSpec } from 'api/draftSpecs'; -import { getLiveSpecsByLiveSpecId } from 'api/liveSpecsExt'; +import { modifyDraftSpec } from 'api/draftSpecs'; +import { getLiveSpecSpec } from 'api/liveSpecsExt'; import { ProgressStates } from 'components/tables/RowActions/Shared/types'; import { useLoopIndex } from 'context/LoopIndex/useLoopIndex'; import { useMount } from 'react-use'; +import { useBinding_collectionsBeingBackfilled } from 'stores/Binding/hooks'; +import { + getBindingAsFullSource, + getCollectionName, +} from 'utils/workflow-utils'; import { usePreSavePromptStore } from '../../../store/usePreSavePromptStore'; function MarkMaterialization() { const stepIndex = useLoopIndex(); const thisStep = usePreSavePromptStore((state) => state.steps[stepIndex]); + const collectionsBeingBackfilled = useBinding_collectionsBeingBackfilled(); + const [updateStep, updateContext, nextStep, context] = usePreSavePromptStore((state) => [ state.updateStep, @@ -26,13 +33,11 @@ function MarkMaterialization() { }); const updateMaterializationTimestamp = async () => { - const liveSpecResponse = await getLiveSpecsByLiveSpecId( - context.liveSpecId + const liveSpecResponse = await getLiveSpecSpec( + context.backfillTarget.id ); - const catalogName = liveSpecResponse.data?.[0].catalog_name; - - if (liveSpecResponse.error || !catalogName) { + if (liveSpecResponse.error) { updateStep(stepIndex, { error: liveSpecResponse.error, progress: ProgressStates.FAILED, @@ -40,7 +45,9 @@ function MarkMaterialization() { return; } - const draftsResponse = await createEntityDraft(catalogName); + const draftsResponse = await createEntityDraft( + `data flow backfill : ${context.backfillTarget.catalog_name} : create : someKeyGoesHere` + ); const backfilledDraftId = draftsResponse.data?.[0].id; if (draftsResponse.error || !backfilledDraftId) { @@ -57,14 +64,29 @@ function MarkMaterialization() { // Update the spec here const updatedSpec = { - ...liveSpecResponse.data?.[0].spec, + ...liveSpecResponse.data.spec, }; + updatedSpec.bindings.forEach((binding: any) => { + if ( + collectionsBeingBackfilled.includes( + getCollectionName(binding) + ) + ) { + binding.source = getBindingAsFullSource(binding); + binding.source.notBefore = context.timeStopped; + } + }); - const draftedMaterialization = await createDraftSpec( - backfilledDraftId, - catalogName, + const draftedMaterialization = await modifyDraftSpec( updatedSpec, - 'materialization' + { + catalog_name: context.backfillTarget.catalog_name, + draft_id: backfilledDraftId, + spec_type: 'materialization', + }, + undefined, + undefined, + `data flow backfill : ${context.backfillTarget.catalog_name} : update : someKeyGoesHere` ); if (draftedMaterialization.error) { @@ -75,6 +97,10 @@ function MarkMaterialization() { return; } + updateStep(stepIndex, { + progress: ProgressStates.SUCCESS, + }); + nextStep(); }; diff --git a/src/components/shared/Entity/prompts/steps/dataFlowReset/WaitForCaptureStop/index.tsx b/src/components/shared/Entity/prompts/steps/dataFlowReset/WaitForCaptureStop/index.tsx index c274e0fd5..08ffe2dde 100644 --- a/src/components/shared/Entity/prompts/steps/dataFlowReset/WaitForCaptureStop/index.tsx +++ b/src/components/shared/Entity/prompts/steps/dataFlowReset/WaitForCaptureStop/index.tsx @@ -50,12 +50,11 @@ function WaitForCaptureStop() { timeStopped: '01/01/2024', }); - updateStep(stepIndex, { - valid: true, - }); - // Fake timeout to make it feel more async setTimeout(() => { + updateStep(stepIndex, { + valid: true, + }); nextStep(); }, 1000); }; diff --git a/src/components/shared/Entity/prompts/store/Hydrator.tsx b/src/components/shared/Entity/prompts/store/Hydrator.tsx index 49f86d367..539763da9 100644 --- a/src/components/shared/Entity/prompts/store/Hydrator.tsx +++ b/src/components/shared/Entity/prompts/store/Hydrator.tsx @@ -1,12 +1,10 @@ import { BaseComponentProps } from 'types'; -import { useEffect, useRef } from 'react'; +import { useEffect } from 'react'; import { useBinding_backfilledBindings_count } from 'stores/Binding/hooks'; import { useBindingStore } from 'stores/Binding/Store'; import { usePreSavePromptStore } from './usePreSavePromptStore'; function PromptsHydrator({ children }: BaseComponentProps) { - const initialized = useRef(false); - const [initializeSteps] = usePreSavePromptStore((state) => [ state.initializeSteps, ]); @@ -15,13 +13,7 @@ function PromptsHydrator({ children }: BaseComponentProps) { const needsBackfilled = useBinding_backfilledBindings_count(); useEffect(() => { - if (initialized.current) { - console.log('skipping hydration - this will probably break'); - return; - } - initializeSteps(Boolean(backfillDataflow && needsBackfilled)); - initialized.current = true; }, [backfillDataflow, initializeSteps, needsBackfilled]); // eslint-disable-next-line react/jsx-no-useless-fragment diff --git a/src/services/supabase.ts b/src/services/supabase.ts index 0f9423ab1..a5528ba49 100644 --- a/src/services/supabase.ts +++ b/src/services/supabase.ts @@ -311,6 +311,24 @@ export function invokeSupabase<T>(fn: FUNCTIONS, body: any) { } export const insertSupabase = <T = any>( + table: TABLES, + data: any, + noResponse?: boolean +): PromiseLike<CallSupabaseResponse<T>> => { + return supabaseRetry(() => { + const query = supabaseClient + .from(table) + .insert(Array.isArray(data) ? data : [data]); + + if (!noResponse) { + return query.select(); + } + + return query; + }, 'insert').then(handleSuccess<T>, handleFailure); +}; + +export const insertSupabase_noResponse = <T = any>( table: TABLES, data: any ): PromiseLike<CallSupabaseResponse<T>> => { @@ -318,8 +336,7 @@ export const insertSupabase = <T = any>( () => supabaseClient .from(table) - .insert(Array.isArray(data) ? data : [data]) - .select(), + .insert(Array.isArray(data) ? data : [data]), 'insert' ).then(handleSuccess<T>, handleFailure); }; diff --git a/src/stores/Binding/hooks.ts b/src/stores/Binding/hooks.ts index 0f7e79bf0..5a5c2d480 100644 --- a/src/stores/Binding/hooks.ts +++ b/src/stores/Binding/hooks.ts @@ -320,3 +320,13 @@ export const useBinding_backfillSupported = () => useBindingStore((state) => { return state.backfillSupported; }); + +export const useBinding_collectionsBeingBackfilled = () => + useBindingStore( + useShallow((state) => { + return state.backfilledBindings.map((backfilledBinding) => { + return state.resourceConfigs[backfilledBinding].meta + .collectionName; + }); + }) + ); diff --git a/src/utils/workflow-utils.ts b/src/utils/workflow-utils.ts index 3698952a7..43f8c7bf8 100644 --- a/src/utils/workflow-utils.ts +++ b/src/utils/workflow-utils.ts @@ -37,6 +37,17 @@ export const getSourceOrTarget = (binding: any) => { : binding; }; +export const getBindingAsFullSource = (binding: any) => { + const response = getSourceOrTarget(binding); + if (typeof response === 'string') { + return { + name: response, + }; + } + + return getSourceOrTarget(binding); +}; + export const getCollectionNameProp = (entityType: Entity) => { return entityType === 'materialization' ? 'source' : 'target'; }; From 232e8b9c003df6f8f75838f0a9ae3729a2b2edbb Mon Sep 17 00:00:00 2001 From: Travis Jenkins <travis@estuary.dev> Date: Wed, 18 Sep 2024 17:22:20 -0400 Subject: [PATCH 18/66] marking valid so the next step works --- .../Entity/prompts/steps/dataFlowReset/EnableCapture/index.tsx | 1 + .../prompts/steps/dataFlowReset/UpdateMaterialization/index.tsx | 1 + 2 files changed, 2 insertions(+) diff --git a/src/components/shared/Entity/prompts/steps/dataFlowReset/EnableCapture/index.tsx b/src/components/shared/Entity/prompts/steps/dataFlowReset/EnableCapture/index.tsx index 6ad20e001..784ade270 100644 --- a/src/components/shared/Entity/prompts/steps/dataFlowReset/EnableCapture/index.tsx +++ b/src/components/shared/Entity/prompts/steps/dataFlowReset/EnableCapture/index.tsx @@ -50,6 +50,7 @@ function EnableCapture() { updateStep(stepIndex, { progress: ProgressStates.SUCCESS, + valid: true, }); nextStep(); diff --git a/src/components/shared/Entity/prompts/steps/dataFlowReset/UpdateMaterialization/index.tsx b/src/components/shared/Entity/prompts/steps/dataFlowReset/UpdateMaterialization/index.tsx index cff31faad..30b564c6e 100644 --- a/src/components/shared/Entity/prompts/steps/dataFlowReset/UpdateMaterialization/index.tsx +++ b/src/components/shared/Entity/prompts/steps/dataFlowReset/UpdateMaterialization/index.tsx @@ -99,6 +99,7 @@ function MarkMaterialization() { updateStep(stepIndex, { progress: ProgressStates.SUCCESS, + valid: true, }); nextStep(); From 9503ce3f79a015e1d73ca8639bb2138d165498a2 Mon Sep 17 00:00:00 2001 From: Travis Jenkins <travis@estuary.dev> Date: Wed, 18 Sep 2024 17:38:29 -0400 Subject: [PATCH 19/66] Starting to wire up the last step --- .../prompts/steps/preSave/Publish/index.tsx | 91 +++++++++++++++++-- src/services/types.ts | 1 + 2 files changed, 83 insertions(+), 9 deletions(-) diff --git a/src/components/shared/Entity/prompts/steps/preSave/Publish/index.tsx b/src/components/shared/Entity/prompts/steps/preSave/Publish/index.tsx index 88c465889..cd826c13a 100644 --- a/src/components/shared/Entity/prompts/steps/preSave/Publish/index.tsx +++ b/src/components/shared/Entity/prompts/steps/preSave/Publish/index.tsx @@ -1,16 +1,89 @@ +import { createPublication, getPublicationByIdQuery } from 'api/publications'; import Logs from 'components/logs'; +import { ProgressStates } from 'components/tables/RowActions/Shared/types'; +import { useLoopIndex } from 'context/LoopIndex/useLoopIndex'; +import useJobStatusPoller from 'hooks/useJobStatusPoller'; +import { useState } from 'react'; +import { useMount } from 'react-use'; +import { usePreSavePromptStore } from '../../../store/usePreSavePromptStore'; + function Publish() { + const [logsToken, setLogsToken] = useState(null); + + const { jobStatusPoller } = useJobStatusPoller(); + + const stepIndex = useLoopIndex(); + const thisStep = usePreSavePromptStore((state) => state.steps[stepIndex]); + + const [updateStep, context] = usePreSavePromptStore((state) => [ + state.updateStep, + state.context, + ]); + + useMount(() => { + if (thisStep.state.progress === ProgressStates.IDLE) { + updateStep(stepIndex, { + progress: ProgressStates.RUNNING, + }); + + const saveAndPublish = async () => { + // Start publishing it + const publishResponse = await createPublication( + context.backfilledDraftId, + false + ); + + if (publishResponse.error || !publishResponse.data) { + updateStep(stepIndex, { + error: publishResponse.error, + progress: ProgressStates.FAILED, + }); + return; + } + + setLogsToken(publishResponse.data[0].logs_token); + + jobStatusPoller( + getPublicationByIdQuery(publishResponse.data[0].id), + async () => { + updateStep(stepIndex, { + progress: ProgressStates.SUCCESS, + valid: true, + }); + + // nextStep(); + }, + async (error: any) => { + updateStep(stepIndex, { + error, + progress: ProgressStates.FAILED, + valid: false, + }); + // logRocketEvent(CustomEvents.REPUBLISH_PREFIX_FAILED); + } + ); + }; + + void saveAndPublish(); + } else { + console.log('TODO: need to handle showing previous state?'); + } + }); + return ( - <Logs - token={null} - height={350} - loadingLineSeverity="info" - spinnerMessages={{ - stoppedKey: 'preSavePrompt.logs.spinner.stopped', - runningKey: 'preSavePrompt.logs.spinner.running', - }} - /> + <> + explain what is happening + <Logs + token={logsToken} + height={350} + loadingLineSeverity="info" + spinnerMessages={{ + stoppedKey: 'preSavePrompt.logs.spinner.stopped', + runningKey: 'preSavePrompt.logs.spinner.running', + }} + /> + </> ); } diff --git a/src/services/types.ts b/src/services/types.ts index 85372b869..4e74406bd 100644 --- a/src/services/types.ts +++ b/src/services/types.ts @@ -2,6 +2,7 @@ import { PostgrestResponse } from '@supabase/postgrest-js'; export enum CustomEvents { AUTH_SIGNOUT = 'Auth_Signout', + BACKFILL_DATAFLOW = 'Backfill_Dataflow', BINDINGS_EXPECTED_MISSING = 'Bindings_Expected_Missing', BINDINGS_RESOURCE_CONFIG_MISSING = 'Bindings_Resource_Config_Missing', CAPTURE_CREATE = 'Capture_Create', From cdb05a9628abb374d057c0f1c02bb03b6e36c5de Mon Sep 17 00:00:00 2001 From: Travis Jenkins <travis@estuary.dev> Date: Thu, 19 Sep 2024 10:45:59 -0400 Subject: [PATCH 20/66] Updating ChipList to: Support passing in 'sx' so we can override styling when needed Scrolling the element down a little bit when all items are shown --- src/components/shared/ChipList/index.tsx | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/src/components/shared/ChipList/index.tsx b/src/components/shared/ChipList/index.tsx index d0e041686..2dfe2a37d 100644 --- a/src/components/shared/ChipList/index.tsx +++ b/src/components/shared/ChipList/index.tsx @@ -1,12 +1,20 @@ import { Box } from '@mui/material'; -import { useMemo, useState } from 'react'; +import { useEffect, useMemo, useRef, useState } from 'react'; import { useIntl } from 'react-intl'; import { ChipListProps } from './types'; import ChipWrapper from './Wrapper'; -function ChipList({ values, disabled, maxChips, stripPath }: ChipListProps) { +function ChipList({ + values, + disabled, + maxChips, + stripPath, + sx, +}: ChipListProps) { const intl = useIntl(); + const listScroller = useRef<any>(null); + // Format data coming in so we can still pass in a list of strings const formattedValues = useMemo(() => { return values.map((value) => { @@ -37,8 +45,17 @@ function ChipList({ values, disabled, maxChips, stripPath }: ChipListProps) { setMaxRender(valueLength); }; + // When all chips are shown scroll down just a hair to try to make + // sure the user knows that the list is scrollable + useEffect(() => { + if (maxRender === valueLength) { + listScroller.current.scrollTo(undefined, 10); + } + }, [maxRender, valueLength]); + return ( <Box + ref={listScroller} sx={{ display: 'flex', flexWrap: 'wrap', @@ -46,8 +63,9 @@ function ChipList({ values, disabled, maxChips, stripPath }: ChipListProps) { p: 0, m: 0, minWidth: 100, - overflow: 'auto', maxHeight: 100, + overflow: 'auto', + ...sx, }} component="ul" > From e94ff6437124447c3394b0bc0deb31a0663f9d3c Mon Sep 17 00:00:00 2001 From: Travis Jenkins <travis@estuary.dev> Date: Thu, 19 Sep 2024 10:46:21 -0400 Subject: [PATCH 21/66] Making code a bit safer --- src/components/shared/ChipList/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/shared/ChipList/index.tsx b/src/components/shared/ChipList/index.tsx index 2dfe2a37d..196276d1e 100644 --- a/src/components/shared/ChipList/index.tsx +++ b/src/components/shared/ChipList/index.tsx @@ -49,7 +49,7 @@ function ChipList({ // sure the user knows that the list is scrollable useEffect(() => { if (maxRender === valueLength) { - listScroller.current.scrollTo(undefined, 10); + listScroller.current?.scrollTo(undefined, 10); } }, [maxRender, valueLength]); From fd5ad1f51238736b7a3ba9de41a2bfa285971871 Mon Sep 17 00:00:00 2001 From: Travis Jenkins <travis@estuary.dev> Date: Thu, 19 Sep 2024 10:46:32 -0400 Subject: [PATCH 22/66] Typing for chiplist changes --- src/components/shared/ChipList/types.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/components/shared/ChipList/types.ts b/src/components/shared/ChipList/types.ts index 449caf57c..65444d63f 100644 --- a/src/components/shared/ChipList/types.ts +++ b/src/components/shared/ChipList/types.ts @@ -1,3 +1,5 @@ +import { SxProps, Theme } from '@mui/material'; + export interface ChipDisplay { display: string; link?: string; @@ -17,4 +19,5 @@ export interface ChipListProps { disabled?: boolean; maxChips?: number; stripPath?: boolean; + sx?: SxProps<Theme>; } From c46a6564e06f0dd4fd9db72b7ac09a6d6c2a7066 Mon Sep 17 00:00:00 2001 From: Travis Jenkins <travis@estuary.dev> Date: Thu, 19 Sep 2024 10:51:22 -0400 Subject: [PATCH 23/66] styling the options better so the collections don't overlap the option below --- .../SelectMaterialization/Selector.tsx | 2 +- .../SelectMaterialization/SelectorOption.tsx | 26 ++++++++++++++----- 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/src/components/shared/Entity/prompts/steps/dataFlowReset/SelectMaterialization/Selector.tsx b/src/components/shared/Entity/prompts/steps/dataFlowReset/SelectMaterialization/Selector.tsx index 2afec5c15..31de7bb61 100644 --- a/src/components/shared/Entity/prompts/steps/dataFlowReset/SelectMaterialization/Selector.tsx +++ b/src/components/shared/Entity/prompts/steps/dataFlowReset/SelectMaterialization/Selector.tsx @@ -79,7 +79,7 @@ function Selector({ disabled, keys }: RelatedMaterializationSelectorProps) { const RowContent = ( <SelectorOption option={option} - x-react-window-item-height={75} + x-react-window-item-height={100} /> ); diff --git a/src/components/shared/Entity/prompts/steps/dataFlowReset/SelectMaterialization/SelectorOption.tsx b/src/components/shared/Entity/prompts/steps/dataFlowReset/SelectMaterialization/SelectorOption.tsx index c1995e4d9..a1f8ff2a8 100644 --- a/src/components/shared/Entity/prompts/steps/dataFlowReset/SelectMaterialization/SelectorOption.tsx +++ b/src/components/shared/Entity/prompts/steps/dataFlowReset/SelectMaterialization/SelectorOption.tsx @@ -1,4 +1,4 @@ -import { Stack, Typography } from '@mui/material'; +import { Box, Stack, Typography } from '@mui/material'; import ChipList from 'components/shared/ChipList'; import { truncateTextSx } from 'context/Theme'; import { MaterializationSelectorOptionProps } from './types'; @@ -7,12 +7,26 @@ function SelectorOption({ option }: MaterializationSelectorOptionProps) { const { catalog_name, reads_from } = option; return ( - <Stack component="span" direction="column" spacing={1}> - <Typography component="span" sx={truncateTextSx}> - {catalog_name} - </Typography> + <Stack component="span" direction="column" spacing={2}> + <Box + sx={{ + maxHeight: 20, + }} + > + <Typography component="span" sx={truncateTextSx}> + {catalog_name} + </Typography> + </Box> - <ChipList values={reads_from} maxChips={3} /> + <ChipList + values={reads_from} + maxChips={5} + sx={{ + pl: 10, + minHeight: 65, + maxHeight: 65, + }} + /> </Stack> ); } From 653a70142c92afae32aadf043f55b0fcb2ccc9b6 Mon Sep 17 00:00:00 2001 From: Travis Jenkins <travis@estuary.dev> Date: Thu, 19 Sep 2024 11:36:30 -0400 Subject: [PATCH 24/66] Reset the state when leaving the flow --- .../shared/Entity/hooks/useEntityWorkflowHelpers.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/components/shared/Entity/hooks/useEntityWorkflowHelpers.ts b/src/components/shared/Entity/hooks/useEntityWorkflowHelpers.ts index 335f0336b..4b533a6c1 100644 --- a/src/components/shared/Entity/hooks/useEntityWorkflowHelpers.ts +++ b/src/components/shared/Entity/hooks/useEntityWorkflowHelpers.ts @@ -29,6 +29,7 @@ import { useSourceCaptureStore } from 'stores/SourceCapture/Store'; import { useTransformationCreate_resetState } from 'stores/TransformationCreate/hooks'; import { getPathWithParams } from 'utils/misc-utils'; import { snackbarSettings } from 'utils/notification-utils'; +import { usePreSavePromptStore } from '../prompts/store/usePreSavePromptStore'; function useEntityWorkflowHelpers() { const { enqueueSnackbar } = useSnackbar(); @@ -70,6 +71,11 @@ function useEntityWorkflowHelpers() { // Transformation Create Store const resetTransformationCreateState = useTransformationCreate_resetState(); + // PreSave Prompt Store + const resetPreSavePrompt = usePreSavePromptStore( + (state) => state.resetState + ); + const resetState = useCallback(() => { resetFormState(); resetEndpointConfigState(); @@ -80,6 +86,7 @@ function useEntityWorkflowHelpers() { resetSchemaEvolutionState(); resetSourceCapture(); resetTransformationCreateState(); + resetPreSavePrompt(); }, [ resetBindingState, resetBindingsEditorStore, @@ -90,6 +97,7 @@ function useEntityWorkflowHelpers() { resetSchemaEvolutionState, resetSourceCapture, resetTransformationCreateState, + resetPreSavePrompt, ]); const callFailed = useCallback( From 67a86e6a780ab0daf196572c81746546d87e7aab Mon Sep 17 00:00:00 2001 From: Travis Jenkins <travis@estuary.dev> Date: Thu, 19 Sep 2024 11:37:24 -0400 Subject: [PATCH 25/66] Probably best to just store the publication status as a whole Making consts for important keys --- src/components/shared/Entity/prompts/types.ts | 3 +++ src/services/supabase.ts | 6 +++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/components/shared/Entity/prompts/types.ts b/src/components/shared/Entity/prompts/types.ts index d99ac9795..c84694e76 100644 --- a/src/components/shared/Entity/prompts/types.ts +++ b/src/components/shared/Entity/prompts/types.ts @@ -1,5 +1,6 @@ import { EmotionJSX } from '@emotion/react/types/jsx-namespace'; import { PostgrestError } from '@supabase/postgrest-js'; +import { PublicationJobStatus } from 'api/publications'; import { ProgressStates } from 'components/tables/RowActions/Shared/types'; export interface PromptStepState { @@ -17,6 +18,8 @@ export interface PromptStepState { // Used to show logging logsToken?: string; + + publicationStatus?: PublicationJobStatus; } // TODO (dataflow typing) should try to get typing working with the context diff --git a/src/services/supabase.ts b/src/services/supabase.ts index a5528ba49..46789d97e 100644 --- a/src/services/supabase.ts +++ b/src/services/supabase.ts @@ -456,7 +456,11 @@ export const DEFAULT_POLLER_ERROR = { }, }; +export const JOB_TYPE_EMPTY = 'emptyDraft'; +export const JOB_TYPE_FAILURE = 'buildFailed'; +export const JOB_TYPE_SUCCESS = 'emptyDraft'; + // These columns are not always what you want... but okay for a "default" constant -export const JOB_STATUS_SUCCESS = ['emptyDraft', 'success']; +export const JOB_STATUS_SUCCESS = [JOB_TYPE_EMPTY, JOB_TYPE_SUCCESS]; export const JOB_STATUS_COLUMNS = `job_status, logs_token, id`; // END: Poller From 39e905f01373f17d46d8ba3bce117e8dffccda11 Mon Sep 17 00:00:00 2001 From: Travis Jenkins <travis@estuary.dev> Date: Thu, 19 Sep 2024 12:22:07 -0400 Subject: [PATCH 26/66] Adding a "review selections" step as an idea Starting work on handling errors better Trying to get publication populated correctly --- .../shared/Entity/prompts/PreSave/Actions.tsx | 2 +- .../shared/Entity/prompts/PreSave/Title.tsx | 2 +- .../dataFlowReset/DisableCapture/index.tsx | 18 ++- .../dataFlowReset/EnableCapture/index.tsx | 29 ++-- .../ReviewSelection/definition.ts | 12 ++ .../dataFlowReset/ReviewSelection/index.tsx | 132 ++++++++++++++++++ .../SelectMaterialization/BindingReview.tsx | 11 -- .../UpdateMaterialization/index.tsx | 26 ++-- .../prompts/steps/dataFlowReset/shared.ts | 2 + .../prompts/useJobStatusErrorHandler.ts | 29 ++++ src/lang/en-US/Workflows.ts | 1 + 11 files changed, 227 insertions(+), 37 deletions(-) create mode 100644 src/components/shared/Entity/prompts/steps/dataFlowReset/ReviewSelection/definition.ts create mode 100644 src/components/shared/Entity/prompts/steps/dataFlowReset/ReviewSelection/index.tsx create mode 100644 src/components/shared/Entity/prompts/useJobStatusErrorHandler.ts diff --git a/src/components/shared/Entity/prompts/PreSave/Actions.tsx b/src/components/shared/Entity/prompts/PreSave/Actions.tsx index 0b7c82591..9cfd02a3a 100644 --- a/src/components/shared/Entity/prompts/PreSave/Actions.tsx +++ b/src/components/shared/Entity/prompts/PreSave/Actions.tsx @@ -25,7 +25,7 @@ function Actions() { <DialogActions> <Stack direction="row" spacing={2}> <Button - disabled={activeStep > 2} + disabled={activeStep > 3} onClick={onFirstStep ? () => setShow(false) : previousStep} variant="text" > diff --git a/src/components/shared/Entity/prompts/PreSave/Title.tsx b/src/components/shared/Entity/prompts/PreSave/Title.tsx index 8a3d9623a..607b60ddf 100644 --- a/src/components/shared/Entity/prompts/PreSave/Title.tsx +++ b/src/components/shared/Entity/prompts/PreSave/Title.tsx @@ -22,7 +22,7 @@ function Title() { > Please review your changes <IconButton - disabled={activeStep > 2} + disabled={activeStep > 3} onClick={() => { setShow(false); }} diff --git a/src/components/shared/Entity/prompts/steps/dataFlowReset/DisableCapture/index.tsx b/src/components/shared/Entity/prompts/steps/dataFlowReset/DisableCapture/index.tsx index 36caeffaa..0f67aefd9 100644 --- a/src/components/shared/Entity/prompts/steps/dataFlowReset/DisableCapture/index.tsx +++ b/src/components/shared/Entity/prompts/steps/dataFlowReset/DisableCapture/index.tsx @@ -1,5 +1,9 @@ import { modifyDraftSpec } from 'api/draftSpecs'; -import { createPublication, getPublicationByIdQuery } from 'api/publications'; +import { + createPublication, + getPublicationByIdQuery, + PublicationJobStatus, +} from 'api/publications'; import { useEditorStore_id, useEditorStore_queryResponse_draftSpecs, @@ -88,17 +92,23 @@ function DisableCapture() { jobStatusPoller( getPublicationByIdQuery(publishResponse.data[0].id), - async () => { + async (successResponse: PublicationJobStatus) => { updateStep(stepIndex, { + publicationStatus: successResponse, progress: ProgressStates.SUCCESS, valid: true, }); nextStep(); }, - async (error: any) => { + async ( + errorResponse: any //PublicationJobStatus | PostgrestError + ) => { updateStep(stepIndex, { - error, + error: errorResponse, + publicationStatus: errorResponse.logs_token + ? errorResponse + : null, progress: ProgressStates.FAILED, valid: false, }); diff --git a/src/components/shared/Entity/prompts/steps/dataFlowReset/EnableCapture/index.tsx b/src/components/shared/Entity/prompts/steps/dataFlowReset/EnableCapture/index.tsx index 784ade270..e43afd7f6 100644 --- a/src/components/shared/Entity/prompts/steps/dataFlowReset/EnableCapture/index.tsx +++ b/src/components/shared/Entity/prompts/steps/dataFlowReset/EnableCapture/index.tsx @@ -1,9 +1,8 @@ -import { modifyDraftSpec } from 'api/draftSpecs'; +import { createDraftSpec } from 'api/draftSpecs'; import { ProgressStates } from 'components/tables/RowActions/Shared/types'; import { useLoopIndex } from 'context/LoopIndex/useLoopIndex'; import { useMount } from 'react-use'; -import { generateDisabledSpec } from 'utils/entity-utils'; import { usePreSavePromptStore } from '../../../store/usePreSavePromptStore'; function EnableCapture() { @@ -26,17 +25,25 @@ function EnableCapture() { const enableCapture = async () => { // Update the Capture to be disabled - const updateResponse = await modifyDraftSpec( - generateDisabledSpec(context.captureSpec, true, false), - { - draft_id: context.backfilledDraftId, - catalog_name: context.captureName, - spec_type: 'capture', - }, + const updateResponse = await createDraftSpec( + context.backfilledDraftId, + context.captureName, + context.captureSpec, + 'capture', undefined, - undefined, - `data flow backfill : ${context.captureName} : enable : someKeyGoesHere` + false ); + // const updateResponse = await modifyDraftSpec( + // generateDisabledSpec(context.captureSpec, true, false), + // { + // draft_id: context.backfilledDraftId, + // catalog_name: context.captureName, + // spec_type: 'capture', + // }, + // undefined, + // undefined, + // `data flow backfill : ${context.captureName} : enable : someKeyGoesHere` + // ); if (updateResponse.error) { updateStep(stepIndex, { diff --git a/src/components/shared/Entity/prompts/steps/dataFlowReset/ReviewSelection/definition.ts b/src/components/shared/Entity/prompts/steps/dataFlowReset/ReviewSelection/definition.ts new file mode 100644 index 000000000..96a8aa95b --- /dev/null +++ b/src/components/shared/Entity/prompts/steps/dataFlowReset/ReviewSelection/definition.ts @@ -0,0 +1,12 @@ +import { defaultStepState } from '../../../store/shared'; +import { PromptStep } from '../../../types'; +import ReviewSelection from '.'; + +export const ReviewSelectionStep: PromptStep = { + StepComponent: ReviewSelection, + stepLabelMessageId: 'dataFlowReset.reviewSelection.title', + state: { + ...defaultStepState, + valid: true, + }, +}; diff --git a/src/components/shared/Entity/prompts/steps/dataFlowReset/ReviewSelection/index.tsx b/src/components/shared/Entity/prompts/steps/dataFlowReset/ReviewSelection/index.tsx new file mode 100644 index 000000000..8396ac420 --- /dev/null +++ b/src/components/shared/Entity/prompts/steps/dataFlowReset/ReviewSelection/index.tsx @@ -0,0 +1,132 @@ +import { + Stack, + Table, + TableBody, + TableCell, + TableContainer, + TableRow, + Typography, +} from '@mui/material'; +import { useEditorStore_queryResponse_draftSpecs } from 'components/editor/Store/hooks'; +import AlertBox from 'components/shared/AlertBox'; +import ChipList from 'components/shared/ChipList'; +import { useIntl } from 'react-intl'; +import { useBinding_collectionsBeingBackfilled } from 'stores/Binding/hooks'; +import { usePreSavePromptStore } from '../../../store/usePreSavePromptStore'; + +function ReviewSelection() { + const intl = useIntl(); + + const [materializationName] = usePreSavePromptStore((state) => [ + state.context?.backfillTarget?.catalog_name, + ]); + const draftSpecs = useEditorStore_queryResponse_draftSpecs(); + const collectionsBeingBackfilled = useBinding_collectionsBeingBackfilled(); + + return ( + <Stack spacing={2}> + <AlertBox + short + severity="warning" + title={intl.formatMessage({ + id: 'dataFlowReset.warning.title', + })} + > + {intl.formatMessage({ id: 'dataFlowReset.warning.message' })} + </AlertBox> + + <Typography> + Please review the selections you have made and ensure everything + looks correct. If not please click "back" and change them before + continuing. + </Typography> + + <TableContainer> + <Table> + <TableBody> + <TableRow> + <TableCell + sx={{ + background: (theme) => + theme.palette.background.default, + }} + > + {intl.formatMessage({ id: 'terms.capture' })} + </TableCell> + <TableCell>{draftSpecs[0].catalog_name}</TableCell> + </TableRow> + + <TableRow> + <TableCell + sx={{ + background: (theme) => + theme.palette.background.default, + }} + > + {intl.formatMessage({ + id: 'terms.collections', + })} + </TableCell> + <TableCell> + <ChipList + values={collectionsBeingBackfilled} + maxChips={9} + /> + </TableCell> + </TableRow> + + <TableRow> + <TableCell + sx={{ + background: (theme) => + theme.palette.background.default, + }} + > + {intl.formatMessage({ + id: 'terms.materialization', + })} + </TableCell> + <TableCell>{materializationName}</TableCell> + </TableRow> + </TableBody> + </Table> + </TableContainer> + + {/*<TableContainer> + <TableHead> + <TableRow + sx={{ + background: (theme) => + theme.palette.background.default, + }} + > + <TableCell> + {intl.formatMessage({ id: 'terms.capture' })} + </TableCell> + + <TableCell> + {intl.formatMessage({ id: 'terms.collections' })} + </TableCell> + + <TableCell> + {intl.formatMessage({ + id: 'terms.materialization', + })} + </TableCell> + </TableRow> + </TableHead> + <TableBody> + <TableRow> + <TableCell>{draftSpecs[0].catalog_name}</TableCell> + <TableCell> + <ChipList values={collectionsBeingBackfilled} /> + </TableCell> + <TableCell>{materializationName}</TableCell> + </TableRow> + </TableBody> + </TableContainer>*/} + </Stack> + ); +} + +export default ReviewSelection; diff --git a/src/components/shared/Entity/prompts/steps/dataFlowReset/SelectMaterialization/BindingReview.tsx b/src/components/shared/Entity/prompts/steps/dataFlowReset/SelectMaterialization/BindingReview.tsx index 85c7376cd..32a74c22f 100644 --- a/src/components/shared/Entity/prompts/steps/dataFlowReset/SelectMaterialization/BindingReview.tsx +++ b/src/components/shared/Entity/prompts/steps/dataFlowReset/SelectMaterialization/BindingReview.tsx @@ -1,5 +1,4 @@ import { Stack, Typography } from '@mui/material'; -import AlertBox from 'components/shared/AlertBox'; import ChipList from 'components/shared/ChipList'; import { useIntl } from 'react-intl'; import { useBinding_collectionsBeingBackfilled } from 'stores/Binding/hooks'; @@ -12,16 +11,6 @@ function BindingReview() { return ( <Stack direction="column" spacing={2}> - <AlertBox - short - severity="warning" - title={intl.formatMessage({ - id: 'dataFlowReset.warning.title', - })} - > - {intl.formatMessage({ id: 'dataFlowReset.warning.message' })} - </AlertBox> - <Typography> {intl.formatMessage( { id: 'dataFlowReset.step1.message' }, diff --git a/src/components/shared/Entity/prompts/steps/dataFlowReset/UpdateMaterialization/index.tsx b/src/components/shared/Entity/prompts/steps/dataFlowReset/UpdateMaterialization/index.tsx index 30b564c6e..54f24fce6 100644 --- a/src/components/shared/Entity/prompts/steps/dataFlowReset/UpdateMaterialization/index.tsx +++ b/src/components/shared/Entity/prompts/steps/dataFlowReset/UpdateMaterialization/index.tsx @@ -1,5 +1,5 @@ import { createEntityDraft } from 'api/drafts'; -import { modifyDraftSpec } from 'api/draftSpecs'; +import { createDraftSpec } from 'api/draftSpecs'; import { getLiveSpecSpec } from 'api/liveSpecsExt'; import { ProgressStates } from 'components/tables/RowActions/Shared/types'; @@ -77,17 +77,25 @@ function MarkMaterialization() { } }); - const draftedMaterialization = await modifyDraftSpec( + const draftedMaterialization = await createDraftSpec( + backfilledDraftId, + context.backfillTarget.catalog_name, updatedSpec, - { - catalog_name: context.backfillTarget.catalog_name, - draft_id: backfilledDraftId, - spec_type: 'materialization', - }, - undefined, + 'materialization', undefined, - `data flow backfill : ${context.backfillTarget.catalog_name} : update : someKeyGoesHere` + false ); + // const draftedMaterialization = await modifyDraftSpec( + // updatedSpec, + // { + // catalog_name: context.backfillTarget.catalog_name, + // draft_id: backfilledDraftId, + // spec_type: 'materialization', + // }, + // undefined, + // undefined, + // `data flow backfill : ${context.backfillTarget.catalog_name} : update : someKeyGoesHere` + // ); if (draftedMaterialization.error) { updateStep(stepIndex, { diff --git a/src/components/shared/Entity/prompts/steps/dataFlowReset/shared.ts b/src/components/shared/Entity/prompts/steps/dataFlowReset/shared.ts index a49f7da99..13bbfaff5 100644 --- a/src/components/shared/Entity/prompts/steps/dataFlowReset/shared.ts +++ b/src/components/shared/Entity/prompts/steps/dataFlowReset/shared.ts @@ -1,11 +1,13 @@ import { DisableCaptureStep } from './DisableCapture/definition'; import { EnableCaptureStep } from './EnableCapture/definition'; +import { ReviewSelectionStep } from './ReviewSelection/definition'; import { SelectMaterializationStep } from './SelectMaterialization/definition'; import { UpdateMaterializationStep } from './UpdateMaterialization/definition'; import { WaitForCaptureStep } from './WaitForCaptureStop/definition'; export const DataFlowSteps = { selectMaterialization: SelectMaterializationStep, + reviewSelection: ReviewSelectionStep, disableCapture: DisableCaptureStep, waitForCapture: WaitForCaptureStep, updateMaterialization: UpdateMaterializationStep, diff --git a/src/components/shared/Entity/prompts/useJobStatusErrorHandler.ts b/src/components/shared/Entity/prompts/useJobStatusErrorHandler.ts new file mode 100644 index 000000000..0c1f80cec --- /dev/null +++ b/src/components/shared/Entity/prompts/useJobStatusErrorHandler.ts @@ -0,0 +1,29 @@ +import { useCallback } from 'react'; +import { useIntl } from 'react-intl'; +import { BASE_ERROR, JOB_TYPE_FAILURE } from 'services/supabase'; + +function useJobStatusErrorHandler() { + const intl = useIntl(); + + return useCallback( + (error: any) => { + if (error.job_status) { + let message; + if (error.job_status === JOB_TYPE_FAILURE) { + message = intl.formatMessage({ id: '' }); + } + + return { + ...BASE_ERROR, + hint: error.logs_token, + message, + }; + } + + return error; + }, + [intl] + ); +} + +export default useJobStatusErrorHandler; diff --git a/src/lang/en-US/Workflows.ts b/src/lang/en-US/Workflows.ts index 95211b3cd..ecdbd02b2 100644 --- a/src/lang/en-US/Workflows.ts +++ b/src/lang/en-US/Workflows.ts @@ -134,6 +134,7 @@ export const Workflows: Record<string, string> = { 'preSavePrompt.logs.spinner.running': `loading...`, 'dataFlowReset.selectMaterialization.title': `Select materialization for data flow reset`, + 'dataFlowReset.reviewSelection.title': `Review your selections`, 'dataFlowReset.disableCapture.title': `Disable capture`, 'dataFlowReset.waitForCapture.title': `Wait for capture to fully stop`, 'dataFlowReset.updateMaterialization.title': `Update Materialization`, From 3e434643cdc3e3cfcb857d838b768837e34d6b49 Mon Sep 17 00:00:00 2001 From: Travis Jenkins <travis@estuary.dev> Date: Thu, 19 Sep 2024 14:47:08 -0400 Subject: [PATCH 27/66] Refactoring to reduce need of functions --- .../EntityStatOverview/StatOverview.tsx | 70 +++---------------- src/utils/entity-utils.ts | 31 ++++++++ 2 files changed, 39 insertions(+), 62 deletions(-) diff --git a/src/components/home/dashboard/EntityStatOverview/StatOverview.tsx b/src/components/home/dashboard/EntityStatOverview/StatOverview.tsx index 0c0d1e153..5f56bdba1 100644 --- a/src/components/home/dashboard/EntityStatOverview/StatOverview.tsx +++ b/src/components/home/dashboard/EntityStatOverview/StatOverview.tsx @@ -1,15 +1,10 @@ import { Stack, Typography } from '@mui/material'; import { PostgrestError } from '@supabase/postgrest-js'; -import { authenticatedRoutes } from 'app/routes'; import LinkWrapper from 'components/shared/LinkWrapper'; -import { - semiTransparentBackground_blue, - semiTransparentBackground_purple, - semiTransparentBackground_teal, -} from 'context/Theme'; -import { CloudDownload, CloudUpload, DatabaseScript } from 'iconoir-react'; + import { FormattedMessage, useIntl } from 'react-intl'; import { Entity } from 'types'; +import { ENTITY_SETTINGS } from 'utils/entity-utils'; import ActiveEntityCount from './ActiveEntityCount'; import Statistic from './Statistic'; @@ -21,56 +16,6 @@ interface Props { monthlyUsageLoading?: boolean; } -const getEntityPageURLPath = (entityType: string): string => { - if (entityType === 'collection') { - return authenticatedRoutes.collections.fullPath; - } - - if (entityType === 'capture') { - return authenticatedRoutes.captures.fullPath; - } - - return authenticatedRoutes.materializations.fullPath; -}; - -const getTitleId = (entityType: string): string => { - if (entityType === 'collection') { - return 'terms.collections'; - } - - if (entityType === 'capture') { - return 'terms.sources'; - } - - return 'terms.destinations'; -}; - -const getBackgroundColor = ( - entityType: string -): { dark: string; light: string } => { - if (entityType === 'collection') { - return semiTransparentBackground_blue; - } - - if (entityType === 'capture') { - return semiTransparentBackground_teal; - } - - return semiTransparentBackground_purple; -}; - -const getEntityIcon = (entityType: string) => { - if (entityType === 'collection') { - return DatabaseScript; - } - - if (entityType === 'capture') { - return CloudUpload; - } - - return CloudDownload; -}; - export default function StatOverview({ entityType, monthlyUsage, @@ -80,11 +25,12 @@ export default function StatOverview({ }: Props) { const intl = useIntl(); - const route = getEntityPageURLPath(entityType); - const titleId = getTitleId(entityType); - - const background = getBackgroundColor(entityType); - const Icon = getEntityIcon(entityType); + const { + route, + termId: titleId, + background, + Icon, + } = ENTITY_SETTINGS[entityType]; return ( <Stack diff --git a/src/utils/entity-utils.ts b/src/utils/entity-utils.ts index 50fc32a18..fc1f575ac 100644 --- a/src/utils/entity-utils.ts +++ b/src/utils/entity-utils.ts @@ -1,6 +1,37 @@ +import { authenticatedRoutes } from 'app/routes'; +import { + semiTransparentBackground_blue, + semiTransparentBackground_purple, + semiTransparentBackground_teal, +} from 'context/Theme'; +import { CloudDownload, CloudUpload, DatabaseScript } from 'iconoir-react'; import produce from 'immer'; import { specContainsDerivation } from 'utils/misc-utils'; +export const ENTITY_SETTINGS = { + collection: { + Icon: DatabaseScript, + background: semiTransparentBackground_blue, + pluralId: 'terms.collections.plural', + route: authenticatedRoutes.collections.fullPath, + termId: 'terms.collections', + }, + capture: { + Icon: CloudUpload, + background: semiTransparentBackground_teal, + pluralId: 'terms.sources.plural', + route: authenticatedRoutes.captures.fullPath, + termId: 'terms.sources', + }, + materialization: { + Icon: CloudDownload, + background: semiTransparentBackground_purple, + pluralId: 'terms.destinations.plural', + route: authenticatedRoutes.materializations.fullPath, + termId: 'terms.destinations', + }, +}; + export const updateShardDisabled = (draftSpec: any, enabling: boolean) => { draftSpec.shards ??= {}; draftSpec.shards.disable = !enabling; From 6238b8460536acf258360d0f3f8e8e9d9bde7ca1 Mon Sep 17 00:00:00 2001 From: Travis Jenkins <travis@estuary.dev> Date: Thu, 19 Sep 2024 14:48:14 -0400 Subject: [PATCH 28/66] Resetting when closing as doing it in store is not working --- src/components/shared/Entity/prompts/PreSave/Title.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/components/shared/Entity/prompts/PreSave/Title.tsx b/src/components/shared/Entity/prompts/PreSave/Title.tsx index 607b60ddf..5d1215add 100644 --- a/src/components/shared/Entity/prompts/PreSave/Title.tsx +++ b/src/components/shared/Entity/prompts/PreSave/Title.tsx @@ -7,9 +7,10 @@ function Title() { const intl = useIntl(); const theme = useTheme(); - const [activeStep, setShow] = usePreSavePromptStore((state) => [ + const [activeStep, setShow, resetState] = usePreSavePromptStore((state) => [ state.activeStep, state.setShow, + state.resetState, ]); return ( @@ -24,6 +25,7 @@ function Title() { <IconButton disabled={activeStep > 3} onClick={() => { + resetState(); setShow(false); }} > From bdedc89710fb4ee12f4fd1f6333244e21bb5e36b Mon Sep 17 00:00:00 2001 From: Travis Jenkins <travis@estuary.dev> Date: Thu, 19 Sep 2024 14:48:32 -0400 Subject: [PATCH 29/66] Moving hydrator within modal so it is always reset when opening and closing --- src/components/shared/Entity/prompts/PreSave/index.tsx | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/components/shared/Entity/prompts/PreSave/index.tsx b/src/components/shared/Entity/prompts/PreSave/index.tsx index ccb534c8f..64dc3df8a 100644 --- a/src/components/shared/Entity/prompts/PreSave/index.tsx +++ b/src/components/shared/Entity/prompts/PreSave/index.tsx @@ -1,4 +1,5 @@ import { Dialog } from '@mui/material'; +import PromptsHydrator from '../store/Hydrator'; import { usePreSavePromptStore, usePreSavePromptStore_onFirstStep, @@ -13,9 +14,11 @@ function PreSavePrompt() { return ( <Dialog maxWidth={onFirstStep ? 'lg' : 'md'} fullWidth open={show}> - <Title /> - <Content /> - <Actions /> + <PromptsHydrator> + <Title /> + <Content /> + <Actions /> + </PromptsHydrator> </Dialog> ); } From 3a9d9fb71990fccd149d00cffdc23e20d4e22da3 Mon Sep 17 00:00:00 2001 From: Travis Jenkins <travis@estuary.dev> Date: Thu, 19 Sep 2024 14:48:47 -0400 Subject: [PATCH 30/66] Breaking out the review table --- .../ReviewSelection/ReviewTable.tsx | 90 ++++++++++++++ .../dataFlowReset/ReviewSelection/index.tsx | 117 ++---------------- 2 files changed, 99 insertions(+), 108 deletions(-) create mode 100644 src/components/shared/Entity/prompts/steps/dataFlowReset/ReviewSelection/ReviewTable.tsx diff --git a/src/components/shared/Entity/prompts/steps/dataFlowReset/ReviewSelection/ReviewTable.tsx b/src/components/shared/Entity/prompts/steps/dataFlowReset/ReviewSelection/ReviewTable.tsx new file mode 100644 index 000000000..b32486910 --- /dev/null +++ b/src/components/shared/Entity/prompts/steps/dataFlowReset/ReviewSelection/ReviewTable.tsx @@ -0,0 +1,90 @@ +import { + Box, + Stack, + Table, + TableBody, + TableCell, + TableContainer, + TableRow, +} from '@mui/material'; +import { useEditorStore_queryResponse_draftSpecs } from 'components/editor/Store/hooks'; +import ChipList from 'components/shared/ChipList'; +import { useIntl } from 'react-intl'; +import { useBinding_collectionsBeingBackfilled } from 'stores/Binding/hooks'; +import { ENTITY_SETTINGS } from 'utils/entity-utils'; +import { usePreSavePromptStore } from '../../../store/usePreSavePromptStore'; + +function ReviewTable() { + const intl = useIntl(); + + const [materializationName] = usePreSavePromptStore((state) => [ + state.context?.backfillTarget?.catalog_name, + ]); + const draftSpecs = useEditorStore_queryResponse_draftSpecs(); + const collectionsBeingBackfilled = useBinding_collectionsBeingBackfilled(); + + const tableRows = [ + { + entityType: 'capture', + cell: draftSpecs[0].catalog_name, + count: 1, + }, + { + entityType: 'collection', + cell: <ChipList values={collectionsBeingBackfilled} maxChips={9} />, + count: collectionsBeingBackfilled.length, + }, + { + entityType: 'materialization', + cell: materializationName, + count: 1, + }, + ]; + + return ( + <TableContainer> + <Table size="small"> + <TableBody> + {tableRows.map(({ cell, count, entityType }) => { + const { pluralId, Icon } = ENTITY_SETTINGS[entityType]; + + return ( + <TableRow key={`review-table-row-${pluralId}`}> + <TableCell + sx={{ + minHeight: 45, + minWidth: 'fit-content', + maxWidth: 'fit-content', + background: (theme) => + theme.palette.background.default, + }} + > + <Stack + direction="row" + spacing={1} + style={{ + alignItems: 'center', + textTransform: 'capitalize', + }} + > + <Icon style={{ fontSize: 12 }} /> + + <Box> + {intl.formatMessage( + { id: pluralId }, + { count } + )} + </Box> + </Stack> + </TableCell> + <TableCell>{cell}</TableCell> + </TableRow> + ); + })} + </TableBody> + </Table> + </TableContainer> + ); +} + +export default ReviewTable; diff --git a/src/components/shared/Entity/prompts/steps/dataFlowReset/ReviewSelection/index.tsx b/src/components/shared/Entity/prompts/steps/dataFlowReset/ReviewSelection/index.tsx index 8396ac420..04d8248f0 100644 --- a/src/components/shared/Entity/prompts/steps/dataFlowReset/ReviewSelection/index.tsx +++ b/src/components/shared/Entity/prompts/steps/dataFlowReset/ReviewSelection/index.tsx @@ -1,130 +1,31 @@ -import { - Stack, - Table, - TableBody, - TableCell, - TableContainer, - TableRow, - Typography, -} from '@mui/material'; -import { useEditorStore_queryResponse_draftSpecs } from 'components/editor/Store/hooks'; +import { Stack, Typography } from '@mui/material'; +import MessageWithLink from 'components/content/MessageWithLink'; import AlertBox from 'components/shared/AlertBox'; -import ChipList from 'components/shared/ChipList'; import { useIntl } from 'react-intl'; -import { useBinding_collectionsBeingBackfilled } from 'stores/Binding/hooks'; -import { usePreSavePromptStore } from '../../../store/usePreSavePromptStore'; +import ReviewTable from './ReviewTable'; function ReviewSelection() { const intl = useIntl(); - const [materializationName] = usePreSavePromptStore((state) => [ - state.context?.backfillTarget?.catalog_name, - ]); - const draftSpecs = useEditorStore_queryResponse_draftSpecs(); - const collectionsBeingBackfilled = useBinding_collectionsBeingBackfilled(); - return ( <Stack spacing={2}> <AlertBox short severity="warning" title={intl.formatMessage({ - id: 'dataFlowReset.warning.title', + id: 'dataFlowReset.reviewSelection.warning.title', })} > - {intl.formatMessage({ id: 'dataFlowReset.warning.message' })} + <MessageWithLink messageID="dataFlowReset.reviewSelection.warning.message" /> </AlertBox> <Typography> - Please review the selections you have made and ensure everything - looks correct. If not please click "back" and change them before - continuing. + {intl.formatMessage({ + id: 'dataFlowReset.reviewSelection.instructions', + })} </Typography> - <TableContainer> - <Table> - <TableBody> - <TableRow> - <TableCell - sx={{ - background: (theme) => - theme.palette.background.default, - }} - > - {intl.formatMessage({ id: 'terms.capture' })} - </TableCell> - <TableCell>{draftSpecs[0].catalog_name}</TableCell> - </TableRow> - - <TableRow> - <TableCell - sx={{ - background: (theme) => - theme.palette.background.default, - }} - > - {intl.formatMessage({ - id: 'terms.collections', - })} - </TableCell> - <TableCell> - <ChipList - values={collectionsBeingBackfilled} - maxChips={9} - /> - </TableCell> - </TableRow> - - <TableRow> - <TableCell - sx={{ - background: (theme) => - theme.palette.background.default, - }} - > - {intl.formatMessage({ - id: 'terms.materialization', - })} - </TableCell> - <TableCell>{materializationName}</TableCell> - </TableRow> - </TableBody> - </Table> - </TableContainer> - - {/*<TableContainer> - <TableHead> - <TableRow - sx={{ - background: (theme) => - theme.palette.background.default, - }} - > - <TableCell> - {intl.formatMessage({ id: 'terms.capture' })} - </TableCell> - - <TableCell> - {intl.formatMessage({ id: 'terms.collections' })} - </TableCell> - - <TableCell> - {intl.formatMessage({ - id: 'terms.materialization', - })} - </TableCell> - </TableRow> - </TableHead> - <TableBody> - <TableRow> - <TableCell>{draftSpecs[0].catalog_name}</TableCell> - <TableCell> - <ChipList values={collectionsBeingBackfilled} /> - </TableCell> - <TableCell>{materializationName}</TableCell> - </TableRow> - </TableBody> - </TableContainer>*/} + <ReviewTable /> </Stack> ); } From 447d2b104d6db1aa7becbaa857dfeb6307e25683 Mon Sep 17 00:00:00 2001 From: Travis Jenkins <travis@estuary.dev> Date: Thu, 19 Sep 2024 14:49:25 -0400 Subject: [PATCH 31/66] Adding more plural support --- src/lang/en-US/CommonMessages.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/lang/en-US/CommonMessages.ts b/src/lang/en-US/CommonMessages.ts index c845ab2ac..1f6d07663 100644 --- a/src/lang/en-US/CommonMessages.ts +++ b/src/lang/en-US/CommonMessages.ts @@ -69,6 +69,8 @@ export const CommonMessages: Record<string, string> = { // is just how react-intl handles it and we might end up rolling our own. 'terms.bindings.plural': `{count, plural, one {binding} other {bindings}}`, 'terms.collections.plural': `{count, plural, one {collection} other {collections}}`, + 'terms.destinations.plural': `{count, plural, one {destination} other {destinations}}`, + 'terms.sources.plural': `{count, plural, one {source} other {sources}}`, // Common fields 'entityPrefix.label': `Prefix`, From 84c8f679a46e2902ff3f201556b272d22f967ce1 Mon Sep 17 00:00:00 2001 From: Travis Jenkins <travis@estuary.dev> Date: Thu, 19 Sep 2024 14:49:44 -0400 Subject: [PATCH 32/66] Updating content as requested --- src/lang/en-US/Workflows.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/lang/en-US/Workflows.ts b/src/lang/en-US/Workflows.ts index ecdbd02b2..51925b687 100644 --- a/src/lang/en-US/Workflows.ts +++ b/src/lang/en-US/Workflows.ts @@ -145,8 +145,11 @@ export const Workflows: Record<string, string> = { 'workflows.collectionSelector.dataFlowBackfill.option': `Backfill Capture`, 'workflows.collectionSelector.dataFlowBackfill.message': `Backfill capture and reset corresponding tables in materialization.`, - 'dataFlowReset.warning.title': `This cannot be undone or stopped`, - 'dataFlowReset.warning.message': `Once this process is started you must stay on this page. Do not click away or reload the page. If you have any issues please contact support immediately as we may need to assist you in recovery.`, + 'dataFlowReset.reviewSelection.warning.title': `Once this process starts, you must stay on the page`, + 'dataFlowReset.reviewSelection.warning.message': `Do not navigate away or reload. If you have any issues, please contact {docLink}`, + 'dataFlowReset.reviewSelection.warning.message.docLink': `support@estuary.dev`, + 'dataFlowReset.reviewSelection.warning.message.docPath': `${CommonMessages['support.email']}`, + 'dataFlowReset.reviewSelection.instructions': `Please confirm you’d like to reset this data flow:`, 'dataFlowReset.step1.message': `The {entityCount} collections to be backfilled`, 'dataFlowReset.editor.warning.title': `Editing disabled`, From 88e71f81a46e85e38858205e035497dbe75289d2 Mon Sep 17 00:00:00 2001 From: Travis Jenkins <travis@estuary.dev> Date: Thu, 19 Sep 2024 14:55:24 -0400 Subject: [PATCH 33/66] styling tweaks --- .../steps/dataFlowReset/ReviewSelection/ReviewTable.tsx | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/components/shared/Entity/prompts/steps/dataFlowReset/ReviewSelection/ReviewTable.tsx b/src/components/shared/Entity/prompts/steps/dataFlowReset/ReviewSelection/ReviewTable.tsx index b32486910..3746d4850 100644 --- a/src/components/shared/Entity/prompts/steps/dataFlowReset/ReviewSelection/ReviewTable.tsx +++ b/src/components/shared/Entity/prompts/steps/dataFlowReset/ReviewSelection/ReviewTable.tsx @@ -17,11 +17,11 @@ import { usePreSavePromptStore } from '../../../store/usePreSavePromptStore'; function ReviewTable() { const intl = useIntl(); + const draftSpecs = useEditorStore_queryResponse_draftSpecs(); + const collectionsBeingBackfilled = useBinding_collectionsBeingBackfilled(); const [materializationName] = usePreSavePromptStore((state) => [ state.context?.backfillTarget?.catalog_name, ]); - const draftSpecs = useEditorStore_queryResponse_draftSpecs(); - const collectionsBeingBackfilled = useBinding_collectionsBeingBackfilled(); const tableRows = [ { @@ -53,8 +53,6 @@ function ReviewTable() { <TableCell sx={{ minHeight: 45, - minWidth: 'fit-content', - maxWidth: 'fit-content', background: (theme) => theme.palette.background.default, }} From 147c999e8284ab18cb1edd9882a4b013ecde8ed5 Mon Sep 17 00:00:00 2001 From: Travis Jenkins <travis@estuary.dev> Date: Thu, 19 Sep 2024 15:40:20 -0400 Subject: [PATCH 34/66] Putting routes nested Moving the show prompt flag over to the form state store Linking to the capture and materialization details Have LinkWrapper handle newWindow links Allowing path overrides on the details navigator --- .../EntityStatOverview/StatOverview.tsx | 4 +- src/components/shared/Entity/Actions/Save.tsx | 13 +++--- .../shared/Entity/prompts/PreSave/Actions.tsx | 18 +++++---- .../shared/Entity/prompts/PreSave/Title.tsx | 8 ++-- .../shared/Entity/prompts/PreSave/index.tsx | 14 ++++--- .../ReviewSelection/ReviewTable.tsx | 40 ++++++++++++++++++- .../shared/Entity/prompts/store/types.ts | 3 -- .../prompts/store/usePreSavePromptStore.ts | 16 +------- src/components/shared/LinkWrapper.tsx | 12 +++++- src/hooks/useDetailsNavigator.ts | 8 ++-- src/stores/FormState/Store.ts | 19 ++++++++- src/stores/FormState/hooks.ts | 18 +++++++++ src/stores/FormState/types.ts | 3 ++ src/utils/entity-utils.ts | 16 ++++++-- 14 files changed, 139 insertions(+), 53 deletions(-) diff --git a/src/components/home/dashboard/EntityStatOverview/StatOverview.tsx b/src/components/home/dashboard/EntityStatOverview/StatOverview.tsx index 5f56bdba1..61f4fa8ab 100644 --- a/src/components/home/dashboard/EntityStatOverview/StatOverview.tsx +++ b/src/components/home/dashboard/EntityStatOverview/StatOverview.tsx @@ -26,7 +26,7 @@ export default function StatOverview({ const intl = useIntl(); const { - route, + routes: { viewAll }, termId: titleId, background, Icon, @@ -63,7 +63,7 @@ export default function StatOverview({ </Typography> </Stack> - <LinkWrapper link={route}> + <LinkWrapper link={viewAll}> <FormattedMessage id="cta.goToAll" /> </LinkWrapper> </Stack> diff --git a/src/components/shared/Entity/Actions/Save.tsx b/src/components/shared/Entity/Actions/Save.tsx index c68de9e2f..292ea16c0 100644 --- a/src/components/shared/Entity/Actions/Save.tsx +++ b/src/components/shared/Entity/Actions/Save.tsx @@ -8,8 +8,10 @@ import { useIntl } from 'react-intl'; import { useBinding_backfilledBindings_count } from 'stores/Binding/hooks'; import { useBindingStore } from 'stores/Binding/Store'; -import { useFormStateStore_isActive } from 'stores/FormState/hooks'; -import { usePreSavePromptStore } from '../prompts/store/usePreSavePromptStore'; +import { + useFormStateStore_isActive, + useFormStateStore_setShowSavePrompt, +} from 'stores/FormState/hooks'; import { EntityCreateSaveButtonProps } from './types'; import useSave from './useSave'; @@ -26,11 +28,12 @@ function EntityCreateSave({ const save = useSave(logEvent, onFailure, dryRun); const isSaving = useEditorStore_isSaving(); - const formActive = useFormStateStore_isActive(); const draftId = useEditorStore_id(); + const formActive = useFormStateStore_isActive(); + const setShowSavePrompt = useFormStateStore_setShowSavePrompt(); + // TODO (data flow reset) - const [setShow] = usePreSavePromptStore((state) => [state.setShow]); const backfillDataflow = useBindingStore((state) => state.backfillDataFlow); const needsBackfilled = useBinding_backfilledBindings_count(); @@ -41,7 +44,7 @@ function EntityCreateSave({ onClick={async () => { // TODO (data flow reset) if (!dryRun && backfillDataflow && needsBackfilled) { - setShow(true); + setShowSavePrompt(true); } else { await save(draftId); } diff --git a/src/components/shared/Entity/prompts/PreSave/Actions.tsx b/src/components/shared/Entity/prompts/PreSave/Actions.tsx index 9cfd02a3a..9f600dd7f 100644 --- a/src/components/shared/Entity/prompts/PreSave/Actions.tsx +++ b/src/components/shared/Entity/prompts/PreSave/Actions.tsx @@ -1,5 +1,6 @@ import { Button, DialogActions, Stack } from '@mui/material'; import { useIntl } from 'react-intl'; +import { useFormStateStore_setShowSavePrompt } from 'stores/FormState/hooks'; import { usePreSavePromptStore, usePreSavePromptStore_onFirstStep, @@ -9,13 +10,10 @@ import { function Actions() { const intl = useIntl(); - const [activeStep, nextStep, previousStep, setShow] = usePreSavePromptStore( - (state) => [ - state.activeStep, - state.nextStep, - state.previousStep, - state.setShow, - ] + const setShowSavePrompt = useFormStateStore_setShowSavePrompt(); + + const [activeStep, nextStep, previousStep] = usePreSavePromptStore( + (state) => [state.activeStep, state.nextStep, state.previousStep] ); const canContinue = usePreSavePromptStore_stepValid(); @@ -26,7 +24,11 @@ function Actions() { <Stack direction="row" spacing={2}> <Button disabled={activeStep > 3} - onClick={onFirstStep ? () => setShow(false) : previousStep} + onClick={ + onFirstStep + ? () => setShowSavePrompt(false) + : previousStep + } variant="text" > {intl.formatMessage({ diff --git a/src/components/shared/Entity/prompts/PreSave/Title.tsx b/src/components/shared/Entity/prompts/PreSave/Title.tsx index 5d1215add..e1863f359 100644 --- a/src/components/shared/Entity/prompts/PreSave/Title.tsx +++ b/src/components/shared/Entity/prompts/PreSave/Title.tsx @@ -1,15 +1,17 @@ import { DialogTitle, IconButton, useTheme } from '@mui/material'; import { Xmark } from 'iconoir-react'; import { useIntl } from 'react-intl'; +import { useFormStateStore_setShowSavePrompt } from 'stores/FormState/hooks'; import { usePreSavePromptStore } from '../store/usePreSavePromptStore'; function Title() { const intl = useIntl(); const theme = useTheme(); - const [activeStep, setShow, resetState] = usePreSavePromptStore((state) => [ + const setShowSavePrompt = useFormStateStore_setShowSavePrompt(); + + const [activeStep, resetState] = usePreSavePromptStore((state) => [ state.activeStep, - state.setShow, state.resetState, ]); @@ -26,7 +28,7 @@ function Title() { disabled={activeStep > 3} onClick={() => { resetState(); - setShow(false); + setShowSavePrompt(false); }} > <Xmark diff --git a/src/components/shared/Entity/prompts/PreSave/index.tsx b/src/components/shared/Entity/prompts/PreSave/index.tsx index 64dc3df8a..8a3c9f9a8 100644 --- a/src/components/shared/Entity/prompts/PreSave/index.tsx +++ b/src/components/shared/Entity/prompts/PreSave/index.tsx @@ -1,19 +1,21 @@ import { Dialog } from '@mui/material'; +import { useFormStateStore_showSavePrompt } from 'stores/FormState/hooks'; import PromptsHydrator from '../store/Hydrator'; -import { - usePreSavePromptStore, - usePreSavePromptStore_onFirstStep, -} from '../store/usePreSavePromptStore'; +import { usePreSavePromptStore_onFirstStep } from '../store/usePreSavePromptStore'; import Actions from './Actions'; import Content from './Content'; import Title from './Title'; function PreSavePrompt() { - const [show] = usePreSavePromptStore((state) => [state.show]); + const showSavePrompt = useFormStateStore_showSavePrompt(); const onFirstStep = usePreSavePromptStore_onFirstStep(); return ( - <Dialog maxWidth={onFirstStep ? 'lg' : 'md'} fullWidth open={show}> + <Dialog + maxWidth={onFirstStep ? 'lg' : 'md'} + fullWidth + open={showSavePrompt} + > <PromptsHydrator> <Title /> <Content /> diff --git a/src/components/shared/Entity/prompts/steps/dataFlowReset/ReviewSelection/ReviewTable.tsx b/src/components/shared/Entity/prompts/steps/dataFlowReset/ReviewSelection/ReviewTable.tsx index 3746d4850..a69868818 100644 --- a/src/components/shared/Entity/prompts/steps/dataFlowReset/ReviewSelection/ReviewTable.tsx +++ b/src/components/shared/Entity/prompts/steps/dataFlowReset/ReviewSelection/ReviewTable.tsx @@ -9,6 +9,8 @@ import { } from '@mui/material'; import { useEditorStore_queryResponse_draftSpecs } from 'components/editor/Store/hooks'; import ChipList from 'components/shared/ChipList'; +import LinkWrapper from 'components/shared/LinkWrapper'; +import useDetailsNavigator from 'hooks/useDetailsNavigator'; import { useIntl } from 'react-intl'; import { useBinding_collectionsBeingBackfilled } from 'stores/Binding/hooks'; import { ENTITY_SETTINGS } from 'utils/entity-utils'; @@ -17,6 +19,8 @@ import { usePreSavePromptStore } from '../../../store/usePreSavePromptStore'; function ReviewTable() { const intl = useIntl(); + const { generatePath } = useDetailsNavigator(''); + const draftSpecs = useEditorStore_queryResponse_draftSpecs(); const collectionsBeingBackfilled = useBinding_collectionsBeingBackfilled(); const [materializationName] = usePreSavePromptStore((state) => [ @@ -26,7 +30,23 @@ function ReviewTable() { const tableRows = [ { entityType: 'capture', - cell: draftSpecs[0].catalog_name, + cell: ( + <LinkWrapper + newWindow + ariaLabel={intl.formatMessage( + { id: 'entityTable.viewDetails.aria' }, + { name: draftSpecs[0].catalog_name } + )} + link={generatePath( + { + catalog_name: draftSpecs[0].catalog_name, + }, + ENTITY_SETTINGS.capture.routes.details + )} + > + {draftSpecs[0].catalog_name} + </LinkWrapper> + ), count: 1, }, { @@ -36,7 +56,23 @@ function ReviewTable() { }, { entityType: 'materialization', - cell: materializationName, + cell: ( + <LinkWrapper + newWindow + ariaLabel={intl.formatMessage( + { id: 'entityTable.viewDetails.aria' }, + { name: materializationName } + )} + link={generatePath( + { + catalog_name: materializationName, + }, + ENTITY_SETTINGS.materialization.routes.details + )} + > + {materializationName} + </LinkWrapper> + ), count: 1, }, ]; diff --git a/src/components/shared/Entity/prompts/store/types.ts b/src/components/shared/Entity/prompts/store/types.ts index 23e25f772..52506eda1 100644 --- a/src/components/shared/Entity/prompts/store/types.ts +++ b/src/components/shared/Entity/prompts/store/types.ts @@ -16,8 +16,5 @@ export interface PreSavePromptStore<T = any> { nextStep: () => void; previousStep: () => void; - show: boolean; - setShow: (data: PreSavePromptStore['show']) => void; - resetState: () => void; } diff --git a/src/components/shared/Entity/prompts/store/usePreSavePromptStore.ts b/src/components/shared/Entity/prompts/store/usePreSavePromptStore.ts index f1b80b590..3b5e8e1c0 100644 --- a/src/components/shared/Entity/prompts/store/usePreSavePromptStore.ts +++ b/src/components/shared/Entity/prompts/store/usePreSavePromptStore.ts @@ -12,10 +12,9 @@ import { PreSavePromptStore } from './types'; const getInitialState = (): Pick< PreSavePromptStore, - 'activeStep' | 'steps' | 'show' | 'context' + 'activeStep' | 'steps' | 'context' > => ({ activeStep: 0, - show: false, steps: [], context: {}, }); @@ -103,19 +102,6 @@ export const usePreSavePromptStore = create<PreSavePromptStore>()( false, 'previousStep' ), - - setShow: (val) => - set( - produce((state: PreSavePromptStore) => { - state.show = val; - - if (!val) { - state.resetState(); - } - }), - false, - 'setShow' - ), }; }, devtoolsOptions('estuary.presave-prompt-store')) ); diff --git a/src/components/shared/LinkWrapper.tsx b/src/components/shared/LinkWrapper.tsx index 25ed53c88..dbefebe10 100644 --- a/src/components/shared/LinkWrapper.tsx +++ b/src/components/shared/LinkWrapper.tsx @@ -1,13 +1,15 @@ import { Link, useMediaQuery, useTheme } from '@mui/material'; import { Link as ReactRouterLink } from 'react-router-dom'; import { BaseComponentProps } from 'types'; +import { OpenNewWindow } from 'iconoir-react'; interface Props extends BaseComponentProps { link: string; ariaLabel?: string; + newWindow?: boolean; } -function LinkWrapper({ ariaLabel, children, link }: Props) { +function LinkWrapper({ ariaLabel, children, link, newWindow }: Props) { const theme = useTheme(); const belowMd = useMediaQuery(theme.breakpoints.down('md')); @@ -15,9 +17,12 @@ function LinkWrapper({ ariaLabel, children, link }: Props) { <Link reloadDocument={false} component={ReactRouterLink} + target={newWindow ? '_blank' : undefined} to={link} aria-label={ariaLabel} sx={{ + display: 'flex', + alignItems: 'center', padding: 1, pl: 0, overflowWrap: belowMd ? 'break-word' : undefined, @@ -25,6 +30,11 @@ function LinkWrapper({ ariaLabel, children, link }: Props) { }} > {children} + {newWindow ? ( + <OpenNewWindow + style={{ height: 15, width: 15, marginLeft: 5 }} + /> + ) : null} </Link> ); } diff --git a/src/hooks/useDetailsNavigator.ts b/src/hooks/useDetailsNavigator.ts index 65ab69afe..dffc28cc4 100644 --- a/src/hooks/useDetailsNavigator.ts +++ b/src/hooks/useDetailsNavigator.ts @@ -11,8 +11,8 @@ function useDetailsNavigator(path: string) { const navigate = useNavigate(); const generatePath = useCallback( - (data: Data) => { - return getPathWithParams(path, { + (data: Data, pathOverride?: string) => { + return getPathWithParams(pathOverride ?? path, { [GlobalSearchParams.CATALOG_NAME]: data.catalog_name, }); }, @@ -20,9 +20,9 @@ function useDetailsNavigator(path: string) { ); const navigateToPath = useCallback( - (data: Data) => { + (data: Data, pathOverride?: string) => { navigate( - getPathWithParams(path, { + getPathWithParams(pathOverride ?? path, { [GlobalSearchParams.CATALOG_NAME]: data.catalog_name, }) ); diff --git a/src/stores/FormState/Store.ts b/src/stores/FormState/Store.ts index 903164d03..b3c6748de 100644 --- a/src/stores/FormState/Store.ts +++ b/src/stores/FormState/Store.ts @@ -87,7 +87,12 @@ const getInitialStateData = ( messagePrefix: MessagePrefixes ): Pick< EntityFormState, - 'formState' | 'isIdle' | 'isActive' | 'messagePrefix' | 'liveSpec' + | 'formState' + | 'isIdle' + | 'isActive' + | 'messagePrefix' + | 'liveSpec' + | 'showSavePrompt' > => ({ formState: initialFormState, @@ -96,6 +101,8 @@ const getInitialStateData = ( liveSpec: null, + showSavePrompt: false, + messagePrefix, }); @@ -199,6 +206,16 @@ const getInitialState = ( ); }, + setShowSavePrompt: (newVal) => { + set( + produce((state: EntityFormState) => { + state.showSavePrompt = newVal; + }), + false, + 'setShowSavePrompt' + ); + }, + resetState: () => { set( getInitialStateData(messagePrefix), diff --git a/src/stores/FormState/hooks.ts b/src/stores/FormState/hooks.ts index 24df6919f..06c87a3b0 100644 --- a/src/stores/FormState/hooks.ts +++ b/src/stores/FormState/hooks.ts @@ -156,3 +156,21 @@ export const useFormStateStore_setLiveSpec = () => { (state) => state.setLiveSpec ); }; + +export const useFormStateStore_showSavePrompt = () => { + const workflow = useEntityWorkflow(); + + return useZustandStore<EntityFormState, EntityFormState['showSavePrompt']>( + storeName(workflow), + (state) => state.showSavePrompt + ); +}; + +export const useFormStateStore_setShowSavePrompt = () => { + const workflow = useEntityWorkflow(); + + return useZustandStore< + EntityFormState, + EntityFormState['setShowSavePrompt'] + >(storeName(workflow), (state) => state.setShowSavePrompt); +}; diff --git a/src/stores/FormState/types.ts b/src/stores/FormState/types.ts index 3738ae3ac..caed62d6d 100644 --- a/src/stores/FormState/types.ts +++ b/src/stores/FormState/types.ts @@ -56,6 +56,9 @@ export interface EntityFormState { liveSpec: Schema | null; setLiveSpec: (data: EntityFormState['liveSpec']) => void; + showSavePrompt: boolean; + setShowSavePrompt: (data: EntityFormState['showSavePrompt']) => void; + updateStatus: (status: FormStatus, background?: boolean) => void; // Misc. diff --git a/src/utils/entity-utils.ts b/src/utils/entity-utils.ts index fc1f575ac..0f0f8be4f 100644 --- a/src/utils/entity-utils.ts +++ b/src/utils/entity-utils.ts @@ -13,21 +13,31 @@ export const ENTITY_SETTINGS = { Icon: DatabaseScript, background: semiTransparentBackground_blue, pluralId: 'terms.collections.plural', - route: authenticatedRoutes.collections.fullPath, + routes: { + details: authenticatedRoutes.collections.details.overview.fullPath, + viewAll: authenticatedRoutes.collections.fullPath, + }, termId: 'terms.collections', }, capture: { Icon: CloudUpload, background: semiTransparentBackground_teal, pluralId: 'terms.sources.plural', - route: authenticatedRoutes.captures.fullPath, + routes: { + details: authenticatedRoutes.captures.details.overview.fullPath, + viewAll: authenticatedRoutes.captures.fullPath, + }, termId: 'terms.sources', }, materialization: { Icon: CloudDownload, background: semiTransparentBackground_purple, pluralId: 'terms.destinations.plural', - route: authenticatedRoutes.materializations.fullPath, + routes: { + details: + authenticatedRoutes.materializations.details.overview.fullPath, + viewAll: authenticatedRoutes.materializations.fullPath, + }, termId: 'terms.destinations', }, }; From 6b0c9037b940611056007471b33448225375b819 Mon Sep 17 00:00:00 2001 From: Travis Jenkins <travis@estuary.dev> Date: Thu, 19 Sep 2024 15:58:17 -0400 Subject: [PATCH 35/66] Sharing the link to details Making code more null/empty --- .../Entity/EntityNameDetailsLink/index.tsx | 33 +++++++++++++++++ .../Entity/EntityNameDetailsLink/types.ts | 5 +++ .../ReviewSelection/ReviewTable.tsx | 36 ++++++++----------- .../prompts/store/usePreSavePromptStore.ts | 2 +- .../tables/cells/EntityNameLink.tsx | 25 ++----------- 5 files changed, 57 insertions(+), 44 deletions(-) create mode 100644 src/components/shared/Entity/EntityNameDetailsLink/index.tsx create mode 100644 src/components/shared/Entity/EntityNameDetailsLink/types.ts diff --git a/src/components/shared/Entity/EntityNameDetailsLink/index.tsx b/src/components/shared/Entity/EntityNameDetailsLink/index.tsx new file mode 100644 index 000000000..8a0b9e83e --- /dev/null +++ b/src/components/shared/Entity/EntityNameDetailsLink/index.tsx @@ -0,0 +1,33 @@ +import LinkWrapper from 'components/shared/LinkWrapper'; +import { Box, Tooltip } from '@mui/material'; +import { useIntl } from 'react-intl'; +import { ViewDetailsProps } from './types'; + +function EntityNameDetailsLink({ name, path, newWindow }: ViewDetailsProps) { + const intl = useIntl(); + + return ( + <Tooltip + placement="bottom" + style={{ maxWidth: 'fit-content' }} + title={intl.formatMessage({ + id: 'entityTable.detailsLink', + })} + > + <Box> + <LinkWrapper + newWindow={newWindow} + ariaLabel={intl.formatMessage( + { id: 'entityTable.viewDetails.aria' }, + { name } + )} + link={path} + > + {name} + </LinkWrapper> + </Box> + </Tooltip> + ); +} + +export default EntityNameDetailsLink; diff --git a/src/components/shared/Entity/EntityNameDetailsLink/types.ts b/src/components/shared/Entity/EntityNameDetailsLink/types.ts new file mode 100644 index 000000000..82d24ae3a --- /dev/null +++ b/src/components/shared/Entity/EntityNameDetailsLink/types.ts @@ -0,0 +1,5 @@ +export interface ViewDetailsProps { + name: string; + path: string; + newWindow?: boolean; +} diff --git a/src/components/shared/Entity/prompts/steps/dataFlowReset/ReviewSelection/ReviewTable.tsx b/src/components/shared/Entity/prompts/steps/dataFlowReset/ReviewSelection/ReviewTable.tsx index a69868818..d478d986d 100644 --- a/src/components/shared/Entity/prompts/steps/dataFlowReset/ReviewSelection/ReviewTable.tsx +++ b/src/components/shared/Entity/prompts/steps/dataFlowReset/ReviewSelection/ReviewTable.tsx @@ -9,7 +9,7 @@ import { } from '@mui/material'; import { useEditorStore_queryResponse_draftSpecs } from 'components/editor/Store/hooks'; import ChipList from 'components/shared/ChipList'; -import LinkWrapper from 'components/shared/LinkWrapper'; +import EntityNameDetailsLink from 'components/shared/Entity/EntityNameDetailsLink'; import useDetailsNavigator from 'hooks/useDetailsNavigator'; import { useIntl } from 'react-intl'; import { useBinding_collectionsBeingBackfilled } from 'stores/Binding/hooks'; @@ -31,21 +31,16 @@ function ReviewTable() { { entityType: 'capture', cell: ( - <LinkWrapper + <EntityNameDetailsLink newWindow - ariaLabel={intl.formatMessage( - { id: 'entityTable.viewDetails.aria' }, - { name: draftSpecs[0].catalog_name } - )} - link={generatePath( + name={draftSpecs[0].catalog_name} + path={generatePath( { catalog_name: draftSpecs[0].catalog_name, }, ENTITY_SETTINGS.capture.routes.details )} - > - {draftSpecs[0].catalog_name} - </LinkWrapper> + /> ), count: 1, }, @@ -57,21 +52,16 @@ function ReviewTable() { { entityType: 'materialization', cell: ( - <LinkWrapper + <EntityNameDetailsLink newWindow - ariaLabel={intl.formatMessage( - { id: 'entityTable.viewDetails.aria' }, - { name: materializationName } - )} - link={generatePath( + name={materializationName} + path={generatePath( { catalog_name: materializationName, }, ENTITY_SETTINGS.materialization.routes.details )} - > - {materializationName} - </LinkWrapper> + /> ), count: 1, }, @@ -85,10 +75,14 @@ function ReviewTable() { const { pluralId, Icon } = ENTITY_SETTINGS[entityType]; return ( - <TableRow key={`review-table-row-${pluralId}`}> + <TableRow + key={`review-table-row-${pluralId}`} + sx={{ height: 50 }} + > <TableCell sx={{ - minHeight: 45, + minWidth: 'fit-content', + width: 0, background: (theme) => theme.palette.background.default, }} diff --git a/src/components/shared/Entity/prompts/store/usePreSavePromptStore.ts b/src/components/shared/Entity/prompts/store/usePreSavePromptStore.ts index 3b5e8e1c0..0883c8299 100644 --- a/src/components/shared/Entity/prompts/store/usePreSavePromptStore.ts +++ b/src/components/shared/Entity/prompts/store/usePreSavePromptStore.ts @@ -116,7 +116,7 @@ export const usePreSavePromptStore_activeStep = () => { export const usePreSavePromptStore_stepValid = () => { return usePreSavePromptStore( - useShallow((state) => state.steps[state.activeStep].state.valid) + useShallow((state) => state.steps[state.activeStep]?.state.valid) ); }; diff --git a/src/components/tables/cells/EntityNameLink.tsx b/src/components/tables/cells/EntityNameLink.tsx index f4b16ba4d..c89217b5f 100644 --- a/src/components/tables/cells/EntityNameLink.tsx +++ b/src/components/tables/cells/EntityNameLink.tsx @@ -1,7 +1,6 @@ -import { Box, Stack, TableCell, Tooltip } from '@mui/material'; -import LinkWrapper from 'components/shared/LinkWrapper'; +import { Stack, TableCell } from '@mui/material'; +import EntityNameDetailsLink from 'components/shared/Entity/EntityNameDetailsLink'; import EntityStatus from 'components/tables/cells/EntityStatus'; -import { useIntl } from 'react-intl'; import { ShardEntityTypes } from 'stores/ShardDetail/types'; interface Props { @@ -17,8 +16,6 @@ function EntityNameLink({ entityStatusTypes, showEntityStatus, }: Props) { - const intl = useIntl(); - return ( <TableCell sx={{ @@ -36,23 +33,7 @@ function EntityNameLink({ <EntityStatus name={name} taskTypes={entityStatusTypes} /> ) : null} - <Tooltip - title={intl.formatMessage({ - id: 'entityTable.detailsLink', - })} - > - <Box> - <LinkWrapper - ariaLabel={intl.formatMessage( - { id: 'entityTable.viewDetails.aria' }, - { name } - )} - link={detailsLink} - > - {name} - </LinkWrapper> - </Box> - </Tooltip> + <EntityNameDetailsLink name={name} path={detailsLink} /> </Stack> </TableCell> ); From 0670fe83653d9794801a0760589aebdf42b7f4f3 Mon Sep 17 00:00:00 2001 From: Travis Jenkins <travis@estuary.dev> Date: Thu, 19 Sep 2024 16:32:35 -0400 Subject: [PATCH 36/66] A bit better error handling and messaging Removing some filler content Adding support for valid and error to be handled by the store --- .../Entity/prompts/PreSave/Content/index.tsx | 50 ++++++++++++------- .../DisableCapture/definition.ts | 3 +- .../dataFlowReset/DisableCapture/index.tsx | 27 +++------- .../dataFlowReset/EnableCapture/index.tsx | 14 +----- .../UpdateMaterialization/index.tsx | 14 +----- .../WaitForCaptureStop/index.tsx | 5 +- .../prompts/store/usePreSavePromptStore.ts | 24 ++++++++- src/components/shared/Entity/prompts/types.ts | 3 +- src/lang/en-US/Workflows.ts | 3 ++ src/services/supabase.ts | 3 +- 10 files changed, 72 insertions(+), 74 deletions(-) diff --git a/src/components/shared/Entity/prompts/PreSave/Content/index.tsx b/src/components/shared/Entity/prompts/PreSave/Content/index.tsx index 4b3ed8b61..5cc4f19c0 100644 --- a/src/components/shared/Entity/prompts/PreSave/Content/index.tsx +++ b/src/components/shared/Entity/prompts/PreSave/Content/index.tsx @@ -1,4 +1,5 @@ import { + Box, DialogContent, LinearProgress, Step, @@ -32,7 +33,7 @@ function Content() { { StepComponent, stepLabelMessageId, - state: { error, progress, logsToken }, + state: { error, progress, publicationStatus }, }, index ) => { @@ -53,24 +54,35 @@ function Content() { <LinearProgress /> ) : null} - <Error - severity="error" - error={error} - condensed - hideTitle - /> - - <ErrorLogs - defaultOpen - logToken={ - Boolean(error) - ? logsToken - : null - } - logProps={{ - fetchAll: true, - }} - /> + {progress === ProgressStates.FAILED ? ( + <> + <Error + severity="error" + error={error} + condensed + hideTitle + /> + <Box> + <ErrorLogs + defaultOpen + logToken={ + Boolean(error) + ? publicationStatus?.logs_token + : null + } + logProps={{ + fetchAll: true, + spinnerMessages: { + runningKey: + 'logs.default', + stoppedKey: + 'logs.noLogs', + }, + }} + /> + </Box> + </> + ) : null} <StepComponent /> </LoopIndexContextProvider> diff --git a/src/components/shared/Entity/prompts/steps/dataFlowReset/DisableCapture/definition.ts b/src/components/shared/Entity/prompts/steps/dataFlowReset/DisableCapture/definition.ts index 057a17696..0c66b7983 100644 --- a/src/components/shared/Entity/prompts/steps/dataFlowReset/DisableCapture/definition.ts +++ b/src/components/shared/Entity/prompts/steps/dataFlowReset/DisableCapture/definition.ts @@ -5,8 +5,7 @@ import DisableCapture from '.'; // interface DisableCaptureStepContext { // pubId: string | null; // captureSpec: JSON | null; -// captureName: string | null; -// logsToken: string | null; +// captureName: string | null; // } export const DisableCaptureStep: PromptStep = { StepComponent: DisableCapture, diff --git a/src/components/shared/Entity/prompts/steps/dataFlowReset/DisableCapture/index.tsx b/src/components/shared/Entity/prompts/steps/dataFlowReset/DisableCapture/index.tsx index 0f67aefd9..2a3e60b89 100644 --- a/src/components/shared/Entity/prompts/steps/dataFlowReset/DisableCapture/index.tsx +++ b/src/components/shared/Entity/prompts/steps/dataFlowReset/DisableCapture/index.tsx @@ -87,7 +87,6 @@ function DisableCapture() { captureName, captureSpec: newSpec, pubId: publishResponse.data[0].id, - logsToken: publishResponse.data[0].logs_token, }); jobStatusPoller( @@ -95,22 +94,18 @@ function DisableCapture() { async (successResponse: PublicationJobStatus) => { updateStep(stepIndex, { publicationStatus: successResponse, - progress: ProgressStates.SUCCESS, - valid: true, }); nextStep(); }, async ( - errorResponse: any //PublicationJobStatus | PostgrestError + failedResponse: any //PublicationJobStatus | PostgrestError ) => { updateStep(stepIndex, { - error: errorResponse, - publicationStatus: errorResponse.logs_token - ? errorResponse + error: failedResponse.error ? failedResponse : null, + publicationStatus: !failedResponse.error + ? failedResponse : null, - progress: ProgressStates.FAILED, - valid: false, }); // logRocketEvent(CustomEvents.REPUBLISH_PREFIX_FAILED); } @@ -123,18 +118,8 @@ function DisableCapture() { } }); - return ( - <>explain what is happening</> - // <Logs - // token={logsToken} - // height={350} - // loadingLineSeverity="info" - // spinnerMessages={{ - // stoppedKey: 'preSavePrompt.logs.spinner.stopped', - // runningKey: 'preSavePrompt.logs.spinner.running', - // }} - // /> - ); + // eslint-disable-next-line react/jsx-no-useless-fragment + return <></>; } export default DisableCapture; diff --git a/src/components/shared/Entity/prompts/steps/dataFlowReset/EnableCapture/index.tsx b/src/components/shared/Entity/prompts/steps/dataFlowReset/EnableCapture/index.tsx index e43afd7f6..2e729c562 100644 --- a/src/components/shared/Entity/prompts/steps/dataFlowReset/EnableCapture/index.tsx +++ b/src/components/shared/Entity/prompts/steps/dataFlowReset/EnableCapture/index.tsx @@ -71,18 +71,8 @@ function EnableCapture() { } }); - return ( - <>explain what is happening</> - // <Logs - // token={logsToken} - // height={350} - // loadingLineSeverity="info" - // spinnerMessages={{ - // stoppedKey: 'preSavePrompt.logs.spinner.stopped', - // runningKey: 'preSavePrompt.logs.spinner.running', - // }} - // /> - ); + // eslint-disable-next-line react/jsx-no-useless-fragment + return <></>; } export default EnableCapture; diff --git a/src/components/shared/Entity/prompts/steps/dataFlowReset/UpdateMaterialization/index.tsx b/src/components/shared/Entity/prompts/steps/dataFlowReset/UpdateMaterialization/index.tsx index 54f24fce6..72eccbc17 100644 --- a/src/components/shared/Entity/prompts/steps/dataFlowReset/UpdateMaterialization/index.tsx +++ b/src/components/shared/Entity/prompts/steps/dataFlowReset/UpdateMaterialization/index.tsx @@ -119,18 +119,8 @@ function MarkMaterialization() { } }); - return ( - <>explain what is happening</> - // <Logs - // token={logsToken} - // height={350} - // loadingLineSeverity="info" - // spinnerMessages={{ - // stoppedKey: 'preSavePrompt.logs.spinner.stopped', - // runningKey: 'preSavePrompt.logs.spinner.running', - // }} - // /> - ); + // eslint-disable-next-line react/jsx-no-useless-fragment + return <></>; } export default MarkMaterialization; diff --git a/src/components/shared/Entity/prompts/steps/dataFlowReset/WaitForCaptureStop/index.tsx b/src/components/shared/Entity/prompts/steps/dataFlowReset/WaitForCaptureStop/index.tsx index 08ffe2dde..d1e13bede 100644 --- a/src/components/shared/Entity/prompts/steps/dataFlowReset/WaitForCaptureStop/index.tsx +++ b/src/components/shared/Entity/prompts/steps/dataFlowReset/WaitForCaptureStop/index.tsx @@ -65,9 +65,8 @@ function WaitForCaptureStop() { } }); - return ( - <>explain what is happening context.liveSpecId = {context.liveSpecId}</> - ); + // eslint-disable-next-line react/jsx-no-useless-fragment + return <></>; } export default WaitForCaptureStop; diff --git a/src/components/shared/Entity/prompts/store/usePreSavePromptStore.ts b/src/components/shared/Entity/prompts/store/usePreSavePromptStore.ts index 0883c8299..bb387508a 100644 --- a/src/components/shared/Entity/prompts/store/usePreSavePromptStore.ts +++ b/src/components/shared/Entity/prompts/store/usePreSavePromptStore.ts @@ -4,6 +4,7 @@ import { devtools } from 'zustand/middleware'; import { devtoolsOptions } from 'utils/store-utils'; import { useShallow } from 'zustand/react/shallow'; import { ProgressStates } from 'components/tables/RowActions/Shared/types'; +import { JOB_STATUS_FAILURE, JOB_STATUS_SUCCESS } from 'services/supabase'; import { PromptStep } from '../types'; import { DataFlowResetSteps } from '../steps/dataFlowReset/shared'; import { ChangeReviewStep } from '../steps/preSave/ChangeReview/definition'; @@ -45,10 +46,29 @@ export const usePreSavePromptStore = create<PreSavePromptStore>()( updateStep: (stepToUpdate, settings) => set( produce((state: PreSavePromptStore) => { - state.steps[stepToUpdate].state = { - ...state.steps[stepToUpdate].state, + const updating = state.steps[stepToUpdate]; + + updating.state = { + ...updating.state, ...settings, }; + + const newStatus = + settings.publicationStatus?.job_status.type; + if (newStatus) { + if (JOB_STATUS_SUCCESS.includes(newStatus)) { + updating.state.progress = + ProgressStates.SUCCESS; + updating.state.valid = true; + } else if (JOB_STATUS_FAILURE.includes(newStatus)) { + updating.state.progress = ProgressStates.FAILED; + updating.state.valid = false; + updating.state.error = { + message: + 'dataFlowReset.errors.publishFailed', + }; + } + } }), false, 'setActiveStep' diff --git a/src/components/shared/Entity/prompts/types.ts b/src/components/shared/Entity/prompts/types.ts index c84694e76..59d64acb6 100644 --- a/src/components/shared/Entity/prompts/types.ts +++ b/src/components/shared/Entity/prompts/types.ts @@ -1,11 +1,10 @@ import { EmotionJSX } from '@emotion/react/types/jsx-namespace'; -import { PostgrestError } from '@supabase/postgrest-js'; import { PublicationJobStatus } from 'api/publications'; import { ProgressStates } from 'components/tables/RowActions/Shared/types'; export interface PromptStepState { // Both server and client side error - error: PostgrestError | null; + error: any | null; // PostgrestError // Stores what the step is currently doing progress: ProgressStates; diff --git a/src/lang/en-US/Workflows.ts b/src/lang/en-US/Workflows.ts index 51925b687..014549132 100644 --- a/src/lang/en-US/Workflows.ts +++ b/src/lang/en-US/Workflows.ts @@ -140,6 +140,8 @@ export const Workflows: Record<string, string> = { 'dataFlowReset.updateMaterialization.title': `Update Materialization`, 'dataFlowReset.enableCapture.title': `Enable capture`, + 'dataFlowReset.errors.publishFailed': `There was a "build failure" on the server`, + // Dataflow reset 'workflows.collectionSelector.dataFlowBackfill.header': `Choose to backfill just your capture or the entire ${CommonMessages['terms.dataFlow']}.`, 'workflows.collectionSelector.dataFlowBackfill.option': `Backfill Capture`, @@ -352,6 +354,7 @@ export const Workflows: Record<string, string> = { // Logs Dialog 'logs.default': ` `, 'logs.paused': `paused`, + 'logs.noLogs': `no logs were found`, 'logs.restartLink': `click here`, 'logs.tooManyEmpty': `Logs for this build may have ended. {restartCTA} to start waiting for new logs again.`, 'logs.networkFailure': `We encountered a problem streaming logs. Please check your network connection and {restartCTA} to start waiting for new logs again.`, diff --git a/src/services/supabase.ts b/src/services/supabase.ts index 46789d97e..0860bd011 100644 --- a/src/services/supabase.ts +++ b/src/services/supabase.ts @@ -458,9 +458,10 @@ export const DEFAULT_POLLER_ERROR = { export const JOB_TYPE_EMPTY = 'emptyDraft'; export const JOB_TYPE_FAILURE = 'buildFailed'; -export const JOB_TYPE_SUCCESS = 'emptyDraft'; +export const JOB_TYPE_SUCCESS = 'success'; // These columns are not always what you want... but okay for a "default" constant +export const JOB_STATUS_FAILURE = [JOB_TYPE_FAILURE]; export const JOB_STATUS_SUCCESS = [JOB_TYPE_EMPTY, JOB_TYPE_SUCCESS]; export const JOB_STATUS_COLUMNS = `job_status, logs_token, id`; // END: Poller From 8ef6fed4726a84a915d1c4b41e1a09902161195a Mon Sep 17 00:00:00 2001 From: Travis Jenkins <travis@estuary.dev> Date: Thu, 19 Sep 2024 16:34:16 -0400 Subject: [PATCH 37/66] clearing our errors when success --- .../shared/Entity/prompts/store/usePreSavePromptStore.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/shared/Entity/prompts/store/usePreSavePromptStore.ts b/src/components/shared/Entity/prompts/store/usePreSavePromptStore.ts index bb387508a..69976646e 100644 --- a/src/components/shared/Entity/prompts/store/usePreSavePromptStore.ts +++ b/src/components/shared/Entity/prompts/store/usePreSavePromptStore.ts @@ -60,6 +60,7 @@ export const usePreSavePromptStore = create<PreSavePromptStore>()( updating.state.progress = ProgressStates.SUCCESS; updating.state.valid = true; + updating.state.error = null; } else if (JOB_STATUS_FAILURE.includes(newStatus)) { updating.state.progress = ProgressStates.FAILED; updating.state.valid = false; From b4eea48ba210c64adad2f27124f826b177270a89 Mon Sep 17 00:00:00 2001 From: Travis Jenkins <travis@estuary.dev> Date: Thu, 19 Sep 2024 17:54:17 -0400 Subject: [PATCH 38/66] Using related collections list Allowing related collections to open in a new window Changing query to just find materializations with Source Capture Updating content to use the right terms Removing log token from the state --- src/api/liveSpecsExt.ts | 56 +++++++++++++++++++ src/components/shared/ChipList/Wrapper.tsx | 8 ++- src/components/shared/ChipList/types.ts | 2 + .../shared/Entity/RelatedCollections.tsx | 4 +- .../Entity/prompts/PreSave/Content/index.tsx | 2 +- .../ReviewSelection/ReviewTable.tsx | 9 ++- .../SelectMaterialization/BindingReview.tsx | 9 ++- .../Materializations.tsx | 21 +++++-- .../SelectMaterialization/Selector.tsx | 2 +- .../SelectMaterialization/types.ts | 10 ++-- src/components/shared/Entity/prompts/types.ts | 3 - .../tables/RowActions/Shared/types.ts | 13 +++-- src/hooks/useLiveSpecsExt.ts | 17 +++--- src/lang/en-US/CommonMessages.ts | 2 + src/lang/en-US/Workflows.ts | 8 +-- src/utils/entity-utils.ts | 3 + 16 files changed, 126 insertions(+), 43 deletions(-) diff --git a/src/api/liveSpecsExt.ts b/src/api/liveSpecsExt.ts index 5c7aeacd8..b4b7fb853 100644 --- a/src/api/liveSpecsExt.ts +++ b/src/api/liveSpecsExt.ts @@ -385,6 +385,60 @@ const getLiveSpecShards = (tenant: string, entityType: Entity) => { .eq('spec_type', entityType); }; +const liveSpecsExtRelatedColumns = ['catalog_name', 'reads_from', 'id']; +export const liveSpecsExtRelatedQuery = liveSpecsExtRelatedColumns.join(','); +export interface LiveSpecsExt_Related { + catalog_name: string; + reads_from: string[]; + id: string; +} +const getLiveSpecsRelatedToMaterialization = async ( + collectionNames: string[] +) => { + // const limiter = pLimit(3); + // const promises = []; + // let index = 0; + + // // TODO (retry) promise generator + // const promiseGenerator = (idx: number) => { + // return supabaseClient + // .from(TABLES.LIVE_SPECS_EXT) + // .select(liveSpecsExtRelatedQuery) + // .eq('spec_type', 'materialization') + // .overlaps( + // 'reads_from', + // collectionNames.slice(idx, idx + CHUNK_SIZE) + // ) + // .returns<LiveSpecsExt_Related[]>(); + // }; + + // while (index < collectionNames.length) { + // const prom = promiseGenerator(index); + // promises.push(limiter(() => prom)); + // index = index + CHUNK_SIZE; + // } + + // const response = await Promise.all(promises); + // const errors = response.filter((r) => r.error); + // return errors[0] ?? response[0]; + + return supabaseClient + .from(TABLES.LIVE_SPECS_EXT) + .select(liveSpecsExtRelatedQuery) + .eq('spec_type', 'materialization') + .overlaps('reads_from', collectionNames) + .returns<LiveSpecsExt_Related[]>(); +}; + +const getLiveSpecsWithRelatedSourceCapture = async (captureName: string) => { + return supabaseClient + .from(TABLES.LIVE_SPECS_EXT) + .select(liveSpecsExtRelatedQuery) + .eq('spec_type', 'materialization') + .or(`spec->>sourceCapture.eq.${captureName}`) + .returns<LiveSpecsExt_Related[]>(); +}; + export { getLiveSpecs_captures, getLiveSpecs_collections, @@ -398,4 +452,6 @@ export { getLiveSpecsByLiveSpecId, getLiveSpecShards, getLiveSpecSpec, + getLiveSpecsRelatedToMaterialization, + getLiveSpecsWithRelatedSourceCapture, }; diff --git a/src/components/shared/ChipList/Wrapper.tsx b/src/components/shared/ChipList/Wrapper.tsx index 186437fdd..310025366 100644 --- a/src/components/shared/ChipList/Wrapper.tsx +++ b/src/components/shared/ChipList/Wrapper.tsx @@ -96,10 +96,14 @@ function ChipWrapper({ const wrappedChip = useMemo(() => { if (val.link) { - return <LinkWrapper link={val.link}>{chip}</LinkWrapper>; + return ( + <LinkWrapper link={val.link} newWindow={val.newWindow}> + {chip} + </LinkWrapper> + ); } return chip; - }, [chip, val.link]); + }, [chip, val.link, val.newWindow]); return ( <ListItem> diff --git a/src/components/shared/ChipList/types.ts b/src/components/shared/ChipList/types.ts index 65444d63f..70f1cc7b5 100644 --- a/src/components/shared/ChipList/types.ts +++ b/src/components/shared/ChipList/types.ts @@ -3,6 +3,7 @@ import { SxProps, Theme } from '@mui/material'; export interface ChipDisplay { display: string; link?: string; + newWindow?: boolean; title?: string; } @@ -18,6 +19,7 @@ export interface ChipListProps { values: string[] | ChipDisplay[]; disabled?: boolean; maxChips?: number; + newWindow?: boolean; stripPath?: boolean; sx?: SxProps<Theme>; } diff --git a/src/components/shared/Entity/RelatedCollections.tsx b/src/components/shared/Entity/RelatedCollections.tsx index d5b552940..ea18cbbd1 100644 --- a/src/components/shared/Entity/RelatedCollections.tsx +++ b/src/components/shared/Entity/RelatedCollections.tsx @@ -5,9 +5,10 @@ import { useIntl } from 'react-intl'; interface Props { collections: string[] | null; + newWindow?: boolean; } -function RelatedCollections({ collections }: Props) { +function RelatedCollections({ collections, newWindow }: Props) { const intl = useIntl(); const { generatePath } = useDetailsNavigator( @@ -24,6 +25,7 @@ function RelatedCollections({ collections }: Props) { link: generatePath({ catalog_name: collection, }), + newWindow, title: intl.formatMessage( { id: 'detailsPanel.details.linkToCollection', diff --git a/src/components/shared/Entity/prompts/PreSave/Content/index.tsx b/src/components/shared/Entity/prompts/PreSave/Content/index.tsx index 5cc4f19c0..c66f38d90 100644 --- a/src/components/shared/Entity/prompts/PreSave/Content/index.tsx +++ b/src/components/shared/Entity/prompts/PreSave/Content/index.tsx @@ -39,7 +39,7 @@ function Content() { ) => { return ( <Step - key={`PreSave-step-${index}`} + key={`PreSave-step-${stepLabelMessageId}-${index}`} completed={progress >= ProgressFinished} > <StepLabel error={Boolean(error)}> diff --git a/src/components/shared/Entity/prompts/steps/dataFlowReset/ReviewSelection/ReviewTable.tsx b/src/components/shared/Entity/prompts/steps/dataFlowReset/ReviewSelection/ReviewTable.tsx index d478d986d..005699b45 100644 --- a/src/components/shared/Entity/prompts/steps/dataFlowReset/ReviewSelection/ReviewTable.tsx +++ b/src/components/shared/Entity/prompts/steps/dataFlowReset/ReviewSelection/ReviewTable.tsx @@ -8,8 +8,8 @@ import { TableRow, } from '@mui/material'; import { useEditorStore_queryResponse_draftSpecs } from 'components/editor/Store/hooks'; -import ChipList from 'components/shared/ChipList'; import EntityNameDetailsLink from 'components/shared/Entity/EntityNameDetailsLink'; +import RelatedCollections from 'components/shared/Entity/RelatedCollections'; import useDetailsNavigator from 'hooks/useDetailsNavigator'; import { useIntl } from 'react-intl'; import { useBinding_collectionsBeingBackfilled } from 'stores/Binding/hooks'; @@ -46,7 +46,12 @@ function ReviewTable() { }, { entityType: 'collection', - cell: <ChipList values={collectionsBeingBackfilled} maxChips={9} />, + cell: ( + <RelatedCollections + collections={collectionsBeingBackfilled} + newWindow + /> + ), count: collectionsBeingBackfilled.length, }, { diff --git a/src/components/shared/Entity/prompts/steps/dataFlowReset/SelectMaterialization/BindingReview.tsx b/src/components/shared/Entity/prompts/steps/dataFlowReset/SelectMaterialization/BindingReview.tsx index 32a74c22f..514d9f421 100644 --- a/src/components/shared/Entity/prompts/steps/dataFlowReset/SelectMaterialization/BindingReview.tsx +++ b/src/components/shared/Entity/prompts/steps/dataFlowReset/SelectMaterialization/BindingReview.tsx @@ -1,5 +1,5 @@ import { Stack, Typography } from '@mui/material'; -import ChipList from 'components/shared/ChipList'; +import RelatedCollections from 'components/shared/Entity/RelatedCollections'; import { useIntl } from 'react-intl'; import { useBinding_collectionsBeingBackfilled } from 'stores/Binding/hooks'; import Materializations from './Materializations'; @@ -19,9 +19,12 @@ function BindingReview() { } )} </Typography> - <ChipList values={collectionsBeingBackfilled} maxChips={10} /> + <RelatedCollections + collections={collectionsBeingBackfilled} + newWindow + /> - <Materializations selected={collectionsBeingBackfilled} /> + <Materializations /> </Stack> ); } diff --git a/src/components/shared/Entity/prompts/steps/dataFlowReset/SelectMaterialization/Materializations.tsx b/src/components/shared/Entity/prompts/steps/dataFlowReset/SelectMaterialization/Materializations.tsx index 4ccc16e0a..2c1f76f10 100644 --- a/src/components/shared/Entity/prompts/steps/dataFlowReset/SelectMaterialization/Materializations.tsx +++ b/src/components/shared/Entity/prompts/steps/dataFlowReset/SelectMaterialization/Materializations.tsx @@ -1,25 +1,34 @@ import { Box, LinearProgress, Typography } from '@mui/material'; +import { useEditorStore_queryResponse_draftSpecs } from 'components/editor/Store/hooks'; import Error from 'components/shared/Error'; import { useLiveSpecsExt_related } from 'hooks/useLiveSpecsExt'; import { useMemo } from 'react'; import { useIntl } from 'react-intl'; import { hasLength } from 'utils/misc-utils'; import Selector from './Selector'; -import { BindingReviewProps } from './types'; -function Materializations({ selected }: BindingReviewProps) { +function Materializations() { const intl = useIntl(); - const { related, error, isValidating } = useLiveSpecsExt_related(selected); + const draftSpecs = useEditorStore_queryResponse_draftSpecs(); + + const { related, error, isValidating } = useLiveSpecsExt_related( + draftSpecs[0].catalog_name + ); const foundData = useMemo(() => hasLength(related), [related]); return ( <Box> <Typography> - {intl.formatMessage({ - id: 'resetDataFlow.materializations.header', - })} + {intl.formatMessage( + { + id: 'resetDataFlow.materializations.header', + }, + { + captureName: draftSpecs[0].catalog_name, + } + )} </Typography> {isValidating ? <LinearProgress /> : null} diff --git a/src/components/shared/Entity/prompts/steps/dataFlowReset/SelectMaterialization/Selector.tsx b/src/components/shared/Entity/prompts/steps/dataFlowReset/SelectMaterialization/Selector.tsx index 31de7bb61..1ffb21af4 100644 --- a/src/components/shared/Entity/prompts/steps/dataFlowReset/SelectMaterialization/Selector.tsx +++ b/src/components/shared/Entity/prompts/steps/dataFlowReset/SelectMaterialization/Selector.tsx @@ -4,7 +4,7 @@ import { ReactNode, useState } from 'react'; import { useIntl } from 'react-intl'; import { useLoopIndex } from 'context/LoopIndex/useLoopIndex'; import { useShallow } from 'zustand/react/shallow'; -import { LiveSpecsExt_Related } from 'hooks/useLiveSpecsExt'; +import { LiveSpecsExt_Related } from 'api/liveSpecsExt'; import { usePreSavePromptStore } from '../../../store/usePreSavePromptStore'; import { RelatedMaterializationSelectorProps } from './types'; import SelectorOption from './SelectorOption'; diff --git a/src/components/shared/Entity/prompts/steps/dataFlowReset/SelectMaterialization/types.ts b/src/components/shared/Entity/prompts/steps/dataFlowReset/SelectMaterialization/types.ts index 28a1e11a2..be2d3b098 100644 --- a/src/components/shared/Entity/prompts/steps/dataFlowReset/SelectMaterialization/types.ts +++ b/src/components/shared/Entity/prompts/steps/dataFlowReset/SelectMaterialization/types.ts @@ -1,10 +1,10 @@ -import { LiveSpecsExt_Related } from 'hooks/useLiveSpecsExt'; +import { LiveSpecsExt_Related } from 'api/liveSpecsExt'; -export interface BindingReviewProps { - selected: string[]; -} +// export interface BindingReviewProps { +// selected: string[]; +// } -export type MaterializationsProps = BindingReviewProps; +// export type MaterializationsProps = BindingReviewProps; export interface RelatedMaterializationSelectorProps { keys: LiveSpecsExt_Related[]; diff --git a/src/components/shared/Entity/prompts/types.ts b/src/components/shared/Entity/prompts/types.ts index 59d64acb6..5c6032118 100644 --- a/src/components/shared/Entity/prompts/types.ts +++ b/src/components/shared/Entity/prompts/types.ts @@ -15,9 +15,6 @@ export interface PromptStepState { // Controls if the user can continue on from this step valid: boolean; - // Used to show logging - logsToken?: string; - publicationStatus?: PublicationJobStatus; } diff --git a/src/components/tables/RowActions/Shared/types.ts b/src/components/tables/RowActions/Shared/types.ts index 508c7acc0..eeaff023d 100644 --- a/src/components/tables/RowActions/Shared/types.ts +++ b/src/components/tables/RowActions/Shared/types.ts @@ -2,14 +2,15 @@ import { ReactNode } from 'react'; export const ProgressFinished = 200; +// Making these high numbers so we have room to put extras between export enum ProgressStates { - IDLE = -1, // new for steps - PAUSED = 0, // Not used - RUNNING = 1, - SKIPPED = 2, + IDLE = -100, // new for steps + PAUSED = 0, // Not used right now + RUNNING = 100, - // Making these high numbers so we have room to put extras between - SUCCESS = 200, + // Here and above is considered finished + SKIPPED = 200, + SUCCESS = 300, FAILED = 400, } diff --git a/src/hooks/useLiveSpecsExt.ts b/src/hooks/useLiveSpecsExt.ts index b0f56894d..82883b180 100644 --- a/src/hooks/useLiveSpecsExt.ts +++ b/src/hooks/useLiveSpecsExt.ts @@ -1,5 +1,9 @@ import { useQuery } from '@supabase-cache-helpers/postgrest-swr'; import { PostgrestError } from '@supabase/postgrest-js'; +import { + liveSpecsExtRelatedQuery, + LiveSpecsExt_Related, +} from 'api/liveSpecsExt'; import { supabaseClient } from 'context/GlobalProviders'; import { useMemo } from 'react'; import { TABLES } from 'services/supabase'; @@ -110,21 +114,16 @@ export function useLiveSpecsExtWithOutSpec( return useLiveSpecsExt(draftId, specType, false); } -const liveSpecsExtRelatedColumns = ['catalog_name', 'reads_from', 'id']; -const liveSpecsExtRelatedQuery = liveSpecsExtRelatedColumns.join(','); -export interface LiveSpecsExt_Related { - catalog_name: string; - reads_from: string[]; - id: string; -} -export function useLiveSpecsExt_related(selected: string[]) { +export function useLiveSpecsExt_related(captureName: string) { const { data, error, isValidating } = useQuery( supabaseClient .from(TABLES.LIVE_SPECS_EXT) .select(liveSpecsExtRelatedQuery) .eq('spec_type', 'materialization') - .overlaps('reads_from', selected) + .or(`spec->>sourceCapture.eq.${captureName}`) .returns<LiveSpecsExt_Related[]>() + // getLiveSpecsRelatedToMaterialization(collectionName) + // getLiveSpecsWithRelatedSourceCapture(captureName) ); return { diff --git a/src/lang/en-US/CommonMessages.ts b/src/lang/en-US/CommonMessages.ts index 1f6d07663..b60dcef46 100644 --- a/src/lang/en-US/CommonMessages.ts +++ b/src/lang/en-US/CommonMessages.ts @@ -60,7 +60,9 @@ export const CommonMessages: Record<string, string> = { 'terms.documentation': `Docs`, 'terms.entity': `Entity`, 'terms.dataFlow': `Data Flow`, + 'terms.source': `Source`, 'terms.sources': `Sources`, + 'terms.destination': `Destination`, 'terms.destinations': `Destinations`, // Terms V2 diff --git a/src/lang/en-US/Workflows.ts b/src/lang/en-US/Workflows.ts index 014549132..ef2ecb6e9 100644 --- a/src/lang/en-US/Workflows.ts +++ b/src/lang/en-US/Workflows.ts @@ -360,8 +360,8 @@ export const Workflows: Record<string, string> = { 'logs.networkFailure': `We encountered a problem streaming logs. Please check your network connection and {restartCTA} to start waiting for new logs again.`, // Reset Data Flow - 'resetDataFlow.materializations.header': `Select which materialization you want backfilled`, - 'resetDataFlow.materializations.empty': `No related materializations`, - 'resetDataFlow.materializations.selector.label': `Materialization to backfill`, - 'resetDataFlow.materializations.selector.helper': `Select one (1) materialization`, + 'resetDataFlow.materializations.header': `Below are ${CommonMessages['terms.sources']} that are linked to "{captureName}" via their Source Capture.`, + 'resetDataFlow.materializations.empty': `No related ${CommonMessages['terms.sources']}`, + 'resetDataFlow.materializations.selector.label': `${CommonMessages['terms.destination']} to backfill`, + 'resetDataFlow.materializations.selector.helper': `Select one (1) ${CommonMessages['terms.destination']}`, }; diff --git a/src/utils/entity-utils.ts b/src/utils/entity-utils.ts index 0f0f8be4f..b202b1740 100644 --- a/src/utils/entity-utils.ts +++ b/src/utils/entity-utils.ts @@ -8,6 +8,9 @@ import { CloudDownload, CloudUpload, DatabaseScript } from 'iconoir-react'; import produce from 'immer'; import { specContainsDerivation } from 'utils/misc-utils'; +// Eventually we'll probably move this out of here as it feels it is beyond the scope +// of "utils". Also, we'll probably end up nesting message keys together and stuff like that +// to keep it a bit easier to visual skim. export const ENTITY_SETTINGS = { collection: { Icon: DatabaseScript, From a1369442478f30d41654b2999de2f845513db332 Mon Sep 17 00:00:00 2001 From: Travis Jenkins <travis@estuary.dev> Date: Tue, 24 Sep 2024 13:36:40 -0400 Subject: [PATCH 39/66] Making sure we only count the enabled bindings in the list --- src/components/editor/Bindings/Backfill/BackfillCount.tsx | 4 ++-- src/components/editor/Bindings/Backfill/index.tsx | 4 ++-- src/stores/Binding/Store.ts | 5 +++++ src/stores/Binding/hooks.ts | 5 +++++ src/stores/Binding/types.ts | 1 + 5 files changed, 15 insertions(+), 4 deletions(-) diff --git a/src/components/editor/Bindings/Backfill/BackfillCount.tsx b/src/components/editor/Bindings/Backfill/BackfillCount.tsx index 254d8514a..1f1065d78 100644 --- a/src/components/editor/Bindings/Backfill/BackfillCount.tsx +++ b/src/components/editor/Bindings/Backfill/BackfillCount.tsx @@ -4,7 +4,7 @@ import { useMemo } from 'react'; import { useIntl } from 'react-intl'; import { useBinding_backfilledBindings_count, - useBinding_collections_count, + useBinding_enabledCollections_count, } from 'stores/Binding/hooks'; import { BackfillCountProps } from './types'; @@ -13,7 +13,7 @@ function BackfillCount({ disabled }: BackfillCountProps) { const entityType = useEntityType(); const backfillCount = useBinding_backfilledBindings_count(); - const bindingsTotal = useBinding_collections_count(); + const bindingsTotal = useBinding_enabledCollections_count(); // Only reason noBackfill is in here is because we are already running the memo on backfillCount change const [noBackfill, itemType_backfill, itemType_bindings] = useMemo(() => { diff --git a/src/components/editor/Bindings/Backfill/index.tsx b/src/components/editor/Bindings/Backfill/index.tsx index e24b56ad3..733f7f233 100644 --- a/src/components/editor/Bindings/Backfill/index.tsx +++ b/src/components/editor/Bindings/Backfill/index.tsx @@ -11,8 +11,8 @@ import { useBinding_currentCollection, useBinding_currentBindingUUID, useBinding_setBackfilledBindings, - useBinding_collections_count, useBinding_backfillSupported, + useBinding_enabledCollections_count, } from 'stores/Binding/hooks'; import { useFormStateStore_isActive, @@ -38,7 +38,7 @@ function Backfill({ description, bindingIndex = -1 }: BackfillProps) { // Binding Store const currentCollection = useBinding_currentCollection(); const currentBindingUUID = useBinding_currentBindingUUID(); - const collectionsCount = useBinding_collections_count(); + const collectionsCount = useBinding_enabledCollections_count(); const allBindingsDisabled = useBinding_allBindingsDisabled(); const backfillAllBindings = useBinding_backfillAllBindings(); diff --git a/src/stores/Binding/Store.ts b/src/stores/Binding/Store.ts index fd425459d..0e2ac464a 100644 --- a/src/stores/Binding/Store.ts +++ b/src/stores/Binding/Store.ts @@ -418,6 +418,11 @@ const getInitialState = ( ({ meta }) => meta.collectionName ), + getEnabledCollections: () => + Object.values(get().resourceConfigs) + .filter(({ meta }) => !meta.disable) + .map(({ meta }) => meta.collectionName), + hydrateState: async ( editWorkflow, entityType, diff --git a/src/stores/Binding/hooks.ts b/src/stores/Binding/hooks.ts index 5a5c2d480..cc6ca61d9 100644 --- a/src/stores/Binding/hooks.ts +++ b/src/stores/Binding/hooks.ts @@ -125,6 +125,11 @@ export const useBinding_collections = () => { export const useBinding_collections_count = () => useBindingStore(useShallow((state) => state.getCollections().length)); +export const useBinding_enabledCollections_count = () => + useBindingStore( + useShallow((state) => state.getEnabledCollections().length) + ); + export const useBinding_toggleDisable = () => { return useBindingStore((state) => state.toggleDisable); }; diff --git a/src/stores/Binding/types.ts b/src/stores/Binding/types.ts index ae30fe154..4ffb902d5 100644 --- a/src/stores/Binding/types.ts +++ b/src/stores/Binding/types.ts @@ -143,6 +143,7 @@ export interface BindingState // Computed Values getCollections: () => string[]; + getEnabledCollections: () => string[]; // Misc. hydrateState: ( From 3c4a4ddbccfb542b8acb023f3539dc906b23efb3 Mon Sep 17 00:00:00 2001 From: Travis Jenkins <travis@estuary.dev> Date: Tue, 24 Sep 2024 13:41:48 -0400 Subject: [PATCH 40/66] cleaning up hook that is not used --- src/stores/Binding/hooks.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/stores/Binding/hooks.ts b/src/stores/Binding/hooks.ts index cc6ca61d9..30f615251 100644 --- a/src/stores/Binding/hooks.ts +++ b/src/stores/Binding/hooks.ts @@ -122,9 +122,6 @@ export const useBinding_collections = () => { return useBindingStore(useShallow((state) => state.getCollections())); }; -export const useBinding_collections_count = () => - useBindingStore(useShallow((state) => state.getCollections().length)); - export const useBinding_enabledCollections_count = () => useBindingStore( useShallow((state) => state.getEnabledCollections().length) From 6c95eab7cf46e0ddfccb20164257913b8b729f98 Mon Sep 17 00:00:00 2001 From: Travis Jenkins <travis@estuary.dev> Date: Tue, 24 Sep 2024 13:50:05 -0400 Subject: [PATCH 41/66] Fixing the warning to display correctly Moving the hook fetch into the component to reduce extra renders --- .../editor/Shards/Disable/Warning.tsx | 89 +++++++++++-------- .../editor/Shards/Disable/index.tsx | 10 +-- 2 files changed, 52 insertions(+), 47 deletions(-) diff --git a/src/components/editor/Shards/Disable/Warning.tsx b/src/components/editor/Shards/Disable/Warning.tsx index 324b288e4..6d4c174eb 100644 --- a/src/components/editor/Shards/Disable/Warning.tsx +++ b/src/components/editor/Shards/Disable/Warning.tsx @@ -3,50 +3,61 @@ import { FormattedMessage } from 'react-intl'; import { useEntityType } from 'context/EntityContext'; import AlertBox from 'components/shared/AlertBox'; +import useGlobalSearchParams, { + GlobalSearchParams, +} from 'hooks/searchParams/useGlobalSearchParams'; function ShardsDisableWarning() { + const forcedToEnable = useGlobalSearchParams<number>( + GlobalSearchParams.FORCED_SHARD_ENABLE + ); + const entityType = useEntityType(); - return ( - <Box - sx={{ - 'mb': 2, - '& .MuiAlertTitle-root': { - textTransform: 'capitalize', - }, - }} - > - <AlertBox - short - severity="warning" - title={ - <FormattedMessage - id="workflows.disable.autoEnabledAlert.title" - values={{ - entityType, - }} - /> - } + if (forcedToEnable === 0) { + return ( + <Box + sx={{ + 'mb': 2, + '& .MuiAlertTitle-root': { + textTransform: 'capitalize', + }, + }} > - <Box> - <FormattedMessage - id="workflows.disable.autoEnabledAlert.message" - values={{ - entityType, - }} - /> - </Box> - <Box> - <FormattedMessage - id="workflows.disable.autoEnabledAlert.instructions" - values={{ - entityType, - }} - /> - </Box> - </AlertBox> - </Box> - ); + <AlertBox + short + severity="warning" + title={ + <FormattedMessage + id="workflows.disable.autoEnabledAlert.title" + values={{ + entityType, + }} + /> + } + > + <Box> + <FormattedMessage + id="workflows.disable.autoEnabledAlert.message" + values={{ + entityType, + }} + /> + </Box> + <Box> + <FormattedMessage + id="workflows.disable.autoEnabledAlert.instructions" + values={{ + entityType, + }} + /> + </Box> + </AlertBox> + </Box> + ); + } + + return null; } export default ShardsDisableWarning; diff --git a/src/components/editor/Shards/Disable/index.tsx b/src/components/editor/Shards/Disable/index.tsx index 03b5b192c..33da93072 100644 --- a/src/components/editor/Shards/Disable/index.tsx +++ b/src/components/editor/Shards/Disable/index.tsx @@ -2,17 +2,11 @@ import { Stack, Typography } from '@mui/material'; import { FormattedMessage } from 'react-intl'; import { useEntityType } from 'context/EntityContext'; import { useEditorStore_persistedDraftId } from 'components/editor/Store/hooks'; -import useGlobalSearchParams, { - GlobalSearchParams, -} from 'hooks/searchParams/useGlobalSearchParams'; + import ShardsDisableForm from './Form'; import ShardsDisableWarning from './Warning'; function ShardsDisable() { - const forcedToEnable = useGlobalSearchParams( - GlobalSearchParams.FORCED_SHARD_ENABLE - ); - const entityType = useEntityType(); const draftId = useEditorStore_persistedDraftId(); @@ -23,7 +17,7 @@ function ShardsDisable() { return ( <> - {forcedToEnable ? <ShardsDisableWarning /> : null} + <ShardsDisableWarning /> <Stack spacing={1} sx={{ mb: 2 }}> <Typography variant="formSectionHeader"> From 1f4a57db5870db92da3d40cfa628f1dbe433fac5 Mon Sep 17 00:00:00 2001 From: Travis Jenkins <travis@estuary.dev> Date: Tue, 24 Sep 2024 14:10:43 -0400 Subject: [PATCH 42/66] Making the save and publish logging and warning shared Moving the LogDialog into a component folder --- .../shared/Entity/LogDialog/Content.tsx | 34 +++++++++++++ .../shared/Entity/LogDialog/index.tsx | 49 +++++++++++++++++++ .../shared/Entity/LogDialog/types.ts | 15 ++++++ 3 files changed, 98 insertions(+) create mode 100644 src/components/shared/Entity/LogDialog/Content.tsx create mode 100644 src/components/shared/Entity/LogDialog/index.tsx create mode 100644 src/components/shared/Entity/LogDialog/types.ts diff --git a/src/components/shared/Entity/LogDialog/Content.tsx b/src/components/shared/Entity/LogDialog/Content.tsx new file mode 100644 index 000000000..da0601f82 --- /dev/null +++ b/src/components/shared/Entity/LogDialog/Content.tsx @@ -0,0 +1,34 @@ +import Logs from 'components/logs'; +import ErrorBoundryWrapper from 'components/shared/ErrorBoundryWrapper'; +import EntityWarnings from '../Warnings'; +import { LogDialogContentProps } from './types'; + +function LogDialogContent({ + spinnerMessageId, + severity, + token, +}: LogDialogContentProps) { + return ( + <> + <EntityWarnings /> + + <ErrorBoundryWrapper> + <Logs + token={token} + height={350} + loadingLineSeverity={severity} + spinnerMessages={ + spinnerMessageId + ? { + stoppedKey: spinnerMessageId, + runningKey: spinnerMessageId, + } + : undefined + } + /> + </ErrorBoundryWrapper> + </> + ); +} + +export default LogDialogContent; diff --git a/src/components/shared/Entity/LogDialog/index.tsx b/src/components/shared/Entity/LogDialog/index.tsx new file mode 100644 index 000000000..a011d8fbd --- /dev/null +++ b/src/components/shared/Entity/LogDialog/index.tsx @@ -0,0 +1,49 @@ +import { + Dialog, + DialogActions, + DialogContent, + DialogTitle, +} from '@mui/material'; +import { useFormStateStore_message } from 'stores/FormState/hooks'; +import LogDialogContent from './Content'; +import { LogDialogProps } from './types'; + +const TITLE_ID = 'logs-dialog-title'; + +function LogDialog({ open, token, actionComponent, title }: LogDialogProps) { + const { key, severity } = useFormStateStore_message(); + + return ( + <Dialog + open={open} + maxWidth="lg" + fullWidth + aria-labelledby={TITLE_ID} + sx={{ + minWidth: (theme) => theme.breakpoints.values.sm, + }} + > + <DialogTitle id={TITLE_ID}>{title}</DialogTitle> + + <DialogContent> + <LogDialogContent + spinnerMessageId={key} + severity={severity} + token={token} + /> + </DialogContent> + + <DialogActions + sx={{ + display: 'flex', + alignItems: 'center', + justifyContent: 'space-between', + }} + > + {actionComponent} + </DialogActions> + </Dialog> + ); +} + +export default LogDialog; diff --git a/src/components/shared/Entity/LogDialog/types.ts b/src/components/shared/Entity/LogDialog/types.ts new file mode 100644 index 000000000..0183893c0 --- /dev/null +++ b/src/components/shared/Entity/LogDialog/types.ts @@ -0,0 +1,15 @@ +import { AlertColor } from '@mui/material'; +import { ReactNode } from 'react'; + +export interface LogDialogProps { + open: boolean; + token: string | null; + title: ReactNode; + actionComponent: ReactNode; +} + +export interface LogDialogContentProps { + spinnerMessageId: string | null; + severity: AlertColor | null; + token: string | null; +} From f72ddf4a1d003d62866c44812619089d1cd7cd83 Mon Sep 17 00:00:00 2001 From: Travis Jenkins <travis@estuary.dev> Date: Tue, 24 Sep 2024 14:16:29 -0400 Subject: [PATCH 43/66] Moved to a component fold --- src/components/shared/Entity/LogDialog.tsx | 70 ---------------------- 1 file changed, 70 deletions(-) delete mode 100644 src/components/shared/Entity/LogDialog.tsx diff --git a/src/components/shared/Entity/LogDialog.tsx b/src/components/shared/Entity/LogDialog.tsx deleted file mode 100644 index 68c9da0fc..000000000 --- a/src/components/shared/Entity/LogDialog.tsx +++ /dev/null @@ -1,70 +0,0 @@ -import { - Dialog, - DialogActions, - DialogContent, - DialogTitle, -} from '@mui/material'; -import Logs from 'components/logs'; -import { ReactNode } from 'react'; -import { useFormStateStore_message } from 'stores/FormState/hooks'; -import ErrorBoundryWrapper from '../ErrorBoundryWrapper'; -import EntityWarnings from './Warnings'; - -interface Props { - open: boolean; - token: string | null; - title: ReactNode; - actionComponent: ReactNode; -} - -const TITLE_ID = 'logs-dialog-title'; - -function LogDialog({ open, token, actionComponent, title }: Props) { - const { key, severity } = useFormStateStore_message(); - - return ( - <Dialog - open={open} - maxWidth="lg" - fullWidth - aria-labelledby={TITLE_ID} - sx={{ - minWidth: (theme) => theme.breakpoints.values.sm, - }} - > - <DialogTitle id={TITLE_ID}>{title}</DialogTitle> - - <DialogContent> - <EntityWarnings /> - - <ErrorBoundryWrapper> - <Logs - token={token} - height={350} - loadingLineSeverity={severity} - spinnerMessages={ - key - ? { - stoppedKey: key, - runningKey: key, - } - : undefined - } - /> - </ErrorBoundryWrapper> - </DialogContent> - - <DialogActions - sx={{ - display: 'flex', - alignItems: 'center', - justifyContent: 'space-between', - }} - > - {actionComponent} - </DialogActions> - </Dialog> - ); -} - -export default LogDialog; From 29b55cea044dd4a748cf068c38425b3e01c49362 Mon Sep 17 00:00:00 2001 From: Travis Jenkins <travis@estuary.dev> Date: Tue, 24 Sep 2024 14:40:30 -0400 Subject: [PATCH 44/66] Updating handling Showing proper error icon in stepper --- .../Entity/prompts/PreSave/Content/index.tsx | 19 ++++++- .../prompts/steps/preSave/Publish/index.tsx | 57 +++++++++---------- 2 files changed, 43 insertions(+), 33 deletions(-) diff --git a/src/components/shared/Entity/prompts/PreSave/Content/index.tsx b/src/components/shared/Entity/prompts/PreSave/Content/index.tsx index c66f38d90..8d45a9d1b 100644 --- a/src/components/shared/Entity/prompts/PreSave/Content/index.tsx +++ b/src/components/shared/Entity/prompts/PreSave/Content/index.tsx @@ -17,6 +17,7 @@ import { useIntl } from 'react-intl'; import Error from 'components/shared/Error'; import ErrorLogs from 'components/shared/Entity/Error/Logs'; import { LoopIndexContextProvider } from 'context/LoopIndex'; +import { XmarkCircle } from 'iconoir-react'; import { usePreSavePromptStore } from '../../store/usePreSavePromptStore'; function Content() { @@ -37,12 +38,24 @@ function Content() { }, index ) => { + const hasError = Boolean(error); + const stepCompleted = progress >= ProgressFinished; + return ( <Step key={`PreSave-step-${stepLabelMessageId}-${index}`} - completed={progress >= ProgressFinished} + completed={stepCompleted} > - <StepLabel error={Boolean(error)}> + <StepLabel + error={hasError} + StepIconProps={ + hasError + ? { + icon: <XmarkCircle />, + } + : undefined + } + > {intl.formatMessage({ id: stepLabelMessageId, })} @@ -66,7 +79,7 @@ function Content() { <ErrorLogs defaultOpen logToken={ - Boolean(error) + hasError ? publicationStatus?.logs_token : null } diff --git a/src/components/shared/Entity/prompts/steps/preSave/Publish/index.tsx b/src/components/shared/Entity/prompts/steps/preSave/Publish/index.tsx index cd826c13a..b9184fd60 100644 --- a/src/components/shared/Entity/prompts/steps/preSave/Publish/index.tsx +++ b/src/components/shared/Entity/prompts/steps/preSave/Publish/index.tsx @@ -1,25 +1,23 @@ -import { createPublication, getPublicationByIdQuery } from 'api/publications'; -import Logs from 'components/logs'; - +import { + createPublication, + getPublicationByIdQuery, + PublicationJobStatus, +} from 'api/publications'; import { ProgressStates } from 'components/tables/RowActions/Shared/types'; import { useLoopIndex } from 'context/LoopIndex/useLoopIndex'; import useJobStatusPoller from 'hooks/useJobStatusPoller'; -import { useState } from 'react'; import { useMount } from 'react-use'; import { usePreSavePromptStore } from '../../../store/usePreSavePromptStore'; function Publish() { - const [logsToken, setLogsToken] = useState(null); - const { jobStatusPoller } = useJobStatusPoller(); const stepIndex = useLoopIndex(); const thisStep = usePreSavePromptStore((state) => state.steps[stepIndex]); - const [updateStep, context] = usePreSavePromptStore((state) => [ - state.updateStep, - state.context, - ]); + const [updateStep, context, updateContext] = usePreSavePromptStore( + (state) => [state.updateStep, state.context, state.updateContext] + ); useMount(() => { if (thisStep.state.progress === ProgressStates.IDLE) { @@ -42,21 +40,27 @@ function Publish() { return; } - setLogsToken(publishResponse.data[0].logs_token); + updateContext({ + pubId: publishResponse.data[0].id, + }); jobStatusPoller( getPublicationByIdQuery(publishResponse.data[0].id), - async () => { + async (successResponse: PublicationJobStatus) => { updateStep(stepIndex, { - progress: ProgressStates.SUCCESS, - valid: true, + publicationStatus: successResponse, }); // nextStep(); }, - async (error: any) => { + async ( + failedResponse: any //PublicationJobStatus | PostgrestError + ) => { updateStep(stepIndex, { - error, + error: failedResponse.error ? failedResponse : null, + publicationStatus: !failedResponse.error + ? failedResponse + : null, progress: ProgressStates.FAILED, valid: false, }); @@ -71,20 +75,13 @@ function Publish() { } }); - return ( - <> - explain what is happening - <Logs - token={logsToken} - height={350} - loadingLineSeverity="info" - spinnerMessages={{ - stoppedKey: 'preSavePrompt.logs.spinner.stopped', - runningKey: 'preSavePrompt.logs.spinner.running', - }} - /> - </> - ); + // eslint-disable-next-line react/jsx-no-useless-fragment + return <></>; + // <LogDialogContent + // spinnerMessageId="preSavePrompt.logs.spinner" + // severity="info" + // token={logsToken} + // /> } export default Publish; From aa61b36c1542ec64b1ab2c1c310fa35e19b815a0 Mon Sep 17 00:00:00 2001 From: Travis Jenkins <travis@estuary.dev> Date: Tue, 24 Sep 2024 15:09:05 -0400 Subject: [PATCH 45/66] Ended up not needing the hook Starting to figure out how to process skipping steps --- .../Materializations.tsx | 8 ++++- .../prompts/useJobStatusErrorHandler.ts | 29 ------------------- 2 files changed, 7 insertions(+), 30 deletions(-) delete mode 100644 src/components/shared/Entity/prompts/useJobStatusErrorHandler.ts diff --git a/src/components/shared/Entity/prompts/steps/dataFlowReset/SelectMaterialization/Materializations.tsx b/src/components/shared/Entity/prompts/steps/dataFlowReset/SelectMaterialization/Materializations.tsx index 2c1f76f10..076e61fc2 100644 --- a/src/components/shared/Entity/prompts/steps/dataFlowReset/SelectMaterialization/Materializations.tsx +++ b/src/components/shared/Entity/prompts/steps/dataFlowReset/SelectMaterialization/Materializations.tsx @@ -2,7 +2,7 @@ import { Box, LinearProgress, Typography } from '@mui/material'; import { useEditorStore_queryResponse_draftSpecs } from 'components/editor/Store/hooks'; import Error from 'components/shared/Error'; import { useLiveSpecsExt_related } from 'hooks/useLiveSpecsExt'; -import { useMemo } from 'react'; +import { useEffect, useMemo } from 'react'; import { useIntl } from 'react-intl'; import { hasLength } from 'utils/misc-utils'; import Selector from './Selector'; @@ -18,6 +18,12 @@ function Materializations() { const foundData = useMemo(() => hasLength(related), [related]); + useEffect(() => { + if (!isValidating || !foundData) { + console.log('we need to skip'); + } + }, [foundData, isValidating]); + return ( <Box> <Typography> diff --git a/src/components/shared/Entity/prompts/useJobStatusErrorHandler.ts b/src/components/shared/Entity/prompts/useJobStatusErrorHandler.ts deleted file mode 100644 index 0c1f80cec..000000000 --- a/src/components/shared/Entity/prompts/useJobStatusErrorHandler.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { useCallback } from 'react'; -import { useIntl } from 'react-intl'; -import { BASE_ERROR, JOB_TYPE_FAILURE } from 'services/supabase'; - -function useJobStatusErrorHandler() { - const intl = useIntl(); - - return useCallback( - (error: any) => { - if (error.job_status) { - let message; - if (error.job_status === JOB_TYPE_FAILURE) { - message = intl.formatMessage({ id: '' }); - } - - return { - ...BASE_ERROR, - hint: error.logs_token, - message, - }; - } - - return error; - }, - [intl] - ); -} - -export default useJobStatusErrorHandler; From aa31af25c8465e2ccf65cfb5daf4641c7d1adfda Mon Sep 17 00:00:00 2001 From: Travis Jenkins <travis@estuary.dev> Date: Tue, 24 Sep 2024 15:12:51 -0400 Subject: [PATCH 46/66] Updating messaging --- .../SelectMaterialization/Materializations.tsx | 13 ++++++++++--- src/lang/en-US/Workflows.ts | 3 ++- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/components/shared/Entity/prompts/steps/dataFlowReset/SelectMaterialization/Materializations.tsx b/src/components/shared/Entity/prompts/steps/dataFlowReset/SelectMaterialization/Materializations.tsx index 076e61fc2..53dd3cc7f 100644 --- a/src/components/shared/Entity/prompts/steps/dataFlowReset/SelectMaterialization/Materializations.tsx +++ b/src/components/shared/Entity/prompts/steps/dataFlowReset/SelectMaterialization/Materializations.tsx @@ -1,5 +1,6 @@ import { Box, LinearProgress, Typography } from '@mui/material'; import { useEditorStore_queryResponse_draftSpecs } from 'components/editor/Store/hooks'; +import AlertBox from 'components/shared/AlertBox'; import Error from 'components/shared/Error'; import { useLiveSpecsExt_related } from 'hooks/useLiveSpecsExt'; import { useEffect, useMemo } from 'react'; @@ -44,11 +45,17 @@ function Materializations() { {!error && foundData ? ( <Selector keys={related} value={null} /> ) : ( - <Box> + <AlertBox + severity="info" + short + title={intl.formatMessage({ + id: 'resetDataFlow.materializations.empty.header', + })} + > {intl.formatMessage({ - id: 'resetDataFlow.materializations.empty', + id: 'resetDataFlow.materializations.empty.message', })} - </Box> + </AlertBox> )} </Box> ); diff --git a/src/lang/en-US/Workflows.ts b/src/lang/en-US/Workflows.ts index ef2ecb6e9..477e064a3 100644 --- a/src/lang/en-US/Workflows.ts +++ b/src/lang/en-US/Workflows.ts @@ -361,7 +361,8 @@ export const Workflows: Record<string, string> = { // Reset Data Flow 'resetDataFlow.materializations.header': `Below are ${CommonMessages['terms.sources']} that are linked to "{captureName}" via their Source Capture.`, - 'resetDataFlow.materializations.empty': `No related ${CommonMessages['terms.sources']}`, + 'resetDataFlow.materializations.empty.header': `No related ${CommonMessages['terms.sources']}`, + 'resetDataFlow.materializations.empty.message': `We currently only support doing a data flow backfill on Capture and Materializations that are linked through the Source Capture property on the Materialization.`, 'resetDataFlow.materializations.selector.label': `${CommonMessages['terms.destination']} to backfill`, 'resetDataFlow.materializations.selector.helper': `Select one (1) ${CommonMessages['terms.destination']}`, }; From 97682e31c6a771068e457d4471d555cc83c26b4c Mon Sep 17 00:00:00 2001 From: Travis Jenkins <travis@estuary.dev> Date: Tue, 24 Sep 2024 17:12:55 -0400 Subject: [PATCH 47/66] Not show logs right away Moving files around a bit to reduce nesting Move the loading indicator into the selector --- .../Entity/prompts/PreSave/Content/index.tsx | 2 - .../SelectMaterialization/BindingReview.tsx | 32 ---------- .../Materializations.tsx | 64 ------------------- .../SelectMaterialization/Selector.tsx | 49 ++++++++++---- .../SelectMaterialization/definition.ts | 2 + .../SelectMaterialization/index.tsx | 36 ++++++++++- .../SelectMaterialization/types.ts | 1 + .../prompts/store/usePreSavePromptStore.ts | 8 +++ src/lang/en-US/Workflows.ts | 5 +- 9 files changed, 84 insertions(+), 115 deletions(-) delete mode 100644 src/components/shared/Entity/prompts/steps/dataFlowReset/SelectMaterialization/BindingReview.tsx delete mode 100644 src/components/shared/Entity/prompts/steps/dataFlowReset/SelectMaterialization/Materializations.tsx diff --git a/src/components/shared/Entity/prompts/PreSave/Content/index.tsx b/src/components/shared/Entity/prompts/PreSave/Content/index.tsx index 8d45a9d1b..690bab316 100644 --- a/src/components/shared/Entity/prompts/PreSave/Content/index.tsx +++ b/src/components/shared/Entity/prompts/PreSave/Content/index.tsx @@ -73,11 +73,9 @@ function Content() { severity="error" error={error} condensed - hideTitle /> <Box> <ErrorLogs - defaultOpen logToken={ hasError ? publicationStatus?.logs_token diff --git a/src/components/shared/Entity/prompts/steps/dataFlowReset/SelectMaterialization/BindingReview.tsx b/src/components/shared/Entity/prompts/steps/dataFlowReset/SelectMaterialization/BindingReview.tsx deleted file mode 100644 index 514d9f421..000000000 --- a/src/components/shared/Entity/prompts/steps/dataFlowReset/SelectMaterialization/BindingReview.tsx +++ /dev/null @@ -1,32 +0,0 @@ -import { Stack, Typography } from '@mui/material'; -import RelatedCollections from 'components/shared/Entity/RelatedCollections'; -import { useIntl } from 'react-intl'; -import { useBinding_collectionsBeingBackfilled } from 'stores/Binding/hooks'; -import Materializations from './Materializations'; - -function BindingReview() { - const intl = useIntl(); - - const collectionsBeingBackfilled = useBinding_collectionsBeingBackfilled(); - - return ( - <Stack direction="column" spacing={2}> - <Typography> - {intl.formatMessage( - { id: 'dataFlowReset.step1.message' }, - { - entityCount: collectionsBeingBackfilled.length, - } - )} - </Typography> - <RelatedCollections - collections={collectionsBeingBackfilled} - newWindow - /> - - <Materializations /> - </Stack> - ); -} - -export default BindingReview; diff --git a/src/components/shared/Entity/prompts/steps/dataFlowReset/SelectMaterialization/Materializations.tsx b/src/components/shared/Entity/prompts/steps/dataFlowReset/SelectMaterialization/Materializations.tsx deleted file mode 100644 index 53dd3cc7f..000000000 --- a/src/components/shared/Entity/prompts/steps/dataFlowReset/SelectMaterialization/Materializations.tsx +++ /dev/null @@ -1,64 +0,0 @@ -import { Box, LinearProgress, Typography } from '@mui/material'; -import { useEditorStore_queryResponse_draftSpecs } from 'components/editor/Store/hooks'; -import AlertBox from 'components/shared/AlertBox'; -import Error from 'components/shared/Error'; -import { useLiveSpecsExt_related } from 'hooks/useLiveSpecsExt'; -import { useEffect, useMemo } from 'react'; -import { useIntl } from 'react-intl'; -import { hasLength } from 'utils/misc-utils'; -import Selector from './Selector'; - -function Materializations() { - const intl = useIntl(); - - const draftSpecs = useEditorStore_queryResponse_draftSpecs(); - - const { related, error, isValidating } = useLiveSpecsExt_related( - draftSpecs[0].catalog_name - ); - - const foundData = useMemo(() => hasLength(related), [related]); - - useEffect(() => { - if (!isValidating || !foundData) { - console.log('we need to skip'); - } - }, [foundData, isValidating]); - - return ( - <Box> - <Typography> - {intl.formatMessage( - { - id: 'resetDataFlow.materializations.header', - }, - { - captureName: draftSpecs[0].catalog_name, - } - )} - </Typography> - - {isValidating ? <LinearProgress /> : null} - - {error ? <Error error={error} condensed /> : null} - - {!error && foundData ? ( - <Selector keys={related} value={null} /> - ) : ( - <AlertBox - severity="info" - short - title={intl.formatMessage({ - id: 'resetDataFlow.materializations.empty.header', - })} - > - {intl.formatMessage({ - id: 'resetDataFlow.materializations.empty.message', - })} - </AlertBox> - )} - </Box> - ); -} - -export default Materializations; diff --git a/src/components/shared/Entity/prompts/steps/dataFlowReset/SelectMaterialization/Selector.tsx b/src/components/shared/Entity/prompts/steps/dataFlowReset/SelectMaterialization/Selector.tsx index 1ffb21af4..ab5cafac6 100644 --- a/src/components/shared/Entity/prompts/steps/dataFlowReset/SelectMaterialization/Selector.tsx +++ b/src/components/shared/Entity/prompts/steps/dataFlowReset/SelectMaterialization/Selector.tsx @@ -1,10 +1,10 @@ -import { Autocomplete, Grid, TextField } from '@mui/material'; +import { Autocomplete, Grid, Skeleton, TextField } from '@mui/material'; import { autoCompleteDefaults_Virtual } from 'components/shared/AutoComplete/DefaultProps'; -import { ReactNode, useState } from 'react'; +import { ReactNode, useEffect, useState } from 'react'; import { useIntl } from 'react-intl'; import { useLoopIndex } from 'context/LoopIndex/useLoopIndex'; -import { useShallow } from 'zustand/react/shallow'; import { LiveSpecsExt_Related } from 'api/liveSpecsExt'; +import AlertBox from 'components/shared/AlertBox'; import { usePreSavePromptStore } from '../../../store/usePreSavePromptStore'; import { RelatedMaterializationSelectorProps } from './types'; import SelectorOption from './SelectorOption'; @@ -12,26 +12,49 @@ import SelectorOption from './SelectorOption'; const getValue = (option: any) => typeof option === 'string' ? option : option?.catalog_name; -function Selector({ disabled, keys }: RelatedMaterializationSelectorProps) { +function Selector({ + disabled, + keys, + loading, +}: RelatedMaterializationSelectorProps) { const intl = useIntl(); - const stepIndex = useLoopIndex(); + const [inputValue, setInputValue] = useState(''); + const [updateStep, updateContext] = usePreSavePromptStore((state) => [ state.updateStep, state.updateContext, ]); - const backfillTarget = usePreSavePromptStore( - useShallow((state) => { - state.context.backfillTarget; - }) - ); + const backfillTarget = usePreSavePromptStore((state) => { + state.context.backfillTarget; + }); - const [inputValue, setInputValue] = useState(''); + useEffect(() => { + updateContext({ + noMaterializations: Boolean(!loading && keys.length === 0), + }); + }, [keys.length, loading, updateContext]); + + if (loading) { + return <Skeleton />; + } if (keys.length === 0) { - return null; + return ( + <AlertBox + severity="info" + short + title={intl.formatMessage({ + id: 'resetDataFlow.materializations.empty.header', + })} + > + {intl.formatMessage({ + id: 'resetDataFlow.materializations.empty.message', + })} + </AlertBox> + ); } return ( @@ -49,6 +72,8 @@ function Selector({ disabled, keys }: RelatedMaterializationSelectorProps) { onChange={(_event, newValue: LiveSpecsExt_Related | null) => { const newBackfillTarget = newValue ? newValue : null; + console.log('newBackfillTarget', newBackfillTarget); + updateStep(stepIndex, { valid: Boolean(newBackfillTarget), }); diff --git a/src/components/shared/Entity/prompts/steps/dataFlowReset/SelectMaterialization/definition.ts b/src/components/shared/Entity/prompts/steps/dataFlowReset/SelectMaterialization/definition.ts index 4c4704fe2..f87b4fa11 100644 --- a/src/components/shared/Entity/prompts/steps/dataFlowReset/SelectMaterialization/definition.ts +++ b/src/components/shared/Entity/prompts/steps/dataFlowReset/SelectMaterialization/definition.ts @@ -4,6 +4,8 @@ import SelectMaterialization from '.'; // interface SelectMaterializationStepContext { // backfillTarget: LiveSpecsExt_Related | null; +// noMaterializations: boolean | null; +// relatedMaterializations: LiveSpecsExt_Related[] | null; // } export const SelectMaterializationStep: PromptStep = { diff --git a/src/components/shared/Entity/prompts/steps/dataFlowReset/SelectMaterialization/index.tsx b/src/components/shared/Entity/prompts/steps/dataFlowReset/SelectMaterialization/index.tsx index d07ecaec4..61ca085bb 100644 --- a/src/components/shared/Entity/prompts/steps/dataFlowReset/SelectMaterialization/index.tsx +++ b/src/components/shared/Entity/prompts/steps/dataFlowReset/SelectMaterialization/index.tsx @@ -1,7 +1,39 @@ -import BindingReview from './BindingReview'; +import { Box, Typography } from '@mui/material'; +import { useEditorStore_queryResponse_draftSpecs } from 'components/editor/Store/hooks'; +import Error from 'components/shared/Error'; +import { useLiveSpecsExt_related } from 'hooks/useLiveSpecsExt'; +import { useIntl } from 'react-intl'; +import Selector from './Selector'; function SelectMaterialization() { - return <BindingReview />; + const intl = useIntl(); + + const draftSpecs = useEditorStore_queryResponse_draftSpecs(); + + const { related, error, isValidating } = useLiveSpecsExt_related( + draftSpecs[0].catalog_name + ); + + return ( + <Box> + <Typography> + {intl.formatMessage( + { + id: 'resetDataFlow.materializations.header', + }, + { + captureName: draftSpecs[0].catalog_name, + } + )} + </Typography> + + {error ? ( + <Error error={error} condensed /> + ) : ( + <Selector keys={related} value={null} loading={isValidating} /> + )} + </Box> + ); } export default SelectMaterialization; diff --git a/src/components/shared/Entity/prompts/steps/dataFlowReset/SelectMaterialization/types.ts b/src/components/shared/Entity/prompts/steps/dataFlowReset/SelectMaterialization/types.ts index be2d3b098..ea2530239 100644 --- a/src/components/shared/Entity/prompts/steps/dataFlowReset/SelectMaterialization/types.ts +++ b/src/components/shared/Entity/prompts/steps/dataFlowReset/SelectMaterialization/types.ts @@ -10,6 +10,7 @@ export interface RelatedMaterializationSelectorProps { keys: LiveSpecsExt_Related[]; value: string | null; disabled?: boolean; + loading?: boolean; onChange?: ( event: any, newValue: string[], diff --git a/src/components/shared/Entity/prompts/store/usePreSavePromptStore.ts b/src/components/shared/Entity/prompts/store/usePreSavePromptStore.ts index 69976646e..07a2ecb2d 100644 --- a/src/components/shared/Entity/prompts/store/usePreSavePromptStore.ts +++ b/src/components/shared/Entity/prompts/store/usePreSavePromptStore.ts @@ -148,3 +148,11 @@ export const usePreSavePromptStore_onFirstStep = () => { }) ); }; + +export const usePreSavePromptStore_onLastStep = () => { + return usePreSavePromptStore( + useShallow((state) => { + return state.activeStep === state.steps.length; + }) + ); +}; diff --git a/src/lang/en-US/Workflows.ts b/src/lang/en-US/Workflows.ts index 477e064a3..7150d9b7a 100644 --- a/src/lang/en-US/Workflows.ts +++ b/src/lang/en-US/Workflows.ts @@ -140,7 +140,7 @@ export const Workflows: Record<string, string> = { 'dataFlowReset.updateMaterialization.title': `Update Materialization`, 'dataFlowReset.enableCapture.title': `Enable capture`, - 'dataFlowReset.errors.publishFailed': `There was a "build failure" on the server`, + 'dataFlowReset.errors.publishFailed': `There was a build failure on the server.`, // Dataflow reset 'workflows.collectionSelector.dataFlowBackfill.header': `Choose to backfill just your capture or the entire ${CommonMessages['terms.dataFlow']}.`, @@ -152,7 +152,6 @@ export const Workflows: Record<string, string> = { 'dataFlowReset.reviewSelection.warning.message.docLink': `support@estuary.dev`, 'dataFlowReset.reviewSelection.warning.message.docPath': `${CommonMessages['support.email']}`, 'dataFlowReset.reviewSelection.instructions': `Please confirm you’d like to reset this data flow:`, - 'dataFlowReset.step1.message': `The {entityCount} collections to be backfilled`, 'dataFlowReset.editor.warning.title': `Editing disabled`, 'dataFlowReset.editor.warning.message': `While backfilling the ${CommonMessages['terms.dataFlow']} you cannot manually edit your spec.`, @@ -360,7 +359,7 @@ export const Workflows: Record<string, string> = { 'logs.networkFailure': `We encountered a problem streaming logs. Please check your network connection and {restartCTA} to start waiting for new logs again.`, // Reset Data Flow - 'resetDataFlow.materializations.header': `Below are ${CommonMessages['terms.sources']} that are linked to "{captureName}" via their Source Capture.`, + 'resetDataFlow.materializations.header': `Below are ${CommonMessages['terms.sources']} that are linked to "{captureName}" via the Source Capture property.`, 'resetDataFlow.materializations.empty.header': `No related ${CommonMessages['terms.sources']}`, 'resetDataFlow.materializations.empty.message': `We currently only support doing a data flow backfill on Capture and Materializations that are linked through the Source Capture property on the Materialization.`, 'resetDataFlow.materializations.selector.label': `${CommonMessages['terms.destination']} to backfill`, From dc81e377d9ae6a814ef450afe1cf69b861c7e1af Mon Sep 17 00:00:00 2001 From: Travis Jenkins <travis@estuary.dev> Date: Tue, 24 Sep 2024 22:01:06 -0400 Subject: [PATCH 48/66] Adding a uuid so make things a bit more traceable --- .../dataFlowReset/DisableCapture/index.tsx | 12 +++++--- .../dataFlowReset/EnableCapture/index.tsx | 28 +++++-------------- .../UpdateMaterialization/index.tsx | 17 +++-------- .../prompts/steps/preSave/Publish/index.tsx | 21 ++++++++++---- .../shared/Entity/prompts/store/types.ts | 1 + .../prompts/store/usePreSavePromptStore.ts | 4 ++- 6 files changed, 38 insertions(+), 45 deletions(-) diff --git a/src/components/shared/Entity/prompts/steps/dataFlowReset/DisableCapture/index.tsx b/src/components/shared/Entity/prompts/steps/dataFlowReset/DisableCapture/index.tsx index 2a3e60b89..f3900c127 100644 --- a/src/components/shared/Entity/prompts/steps/dataFlowReset/DisableCapture/index.tsx +++ b/src/components/shared/Entity/prompts/steps/dataFlowReset/DisableCapture/index.tsx @@ -25,9 +25,13 @@ function DisableCapture() { const stepIndex = useLoopIndex(); const thisStep = usePreSavePromptStore((state) => state.steps[stepIndex]); - const [updateStep, updateContext, nextStep] = usePreSavePromptStore( - (state) => [state.updateStep, state.updateContext, state.nextStep] - ); + const [updateStep, updateContext, nextStep, initUUID] = + usePreSavePromptStore((state) => [ + state.updateStep, + state.updateContext, + state.nextStep, + state.initUUID, + ]); const setFormState = useFormStateStore_setFormState(); @@ -61,7 +65,7 @@ function DisableCapture() { }, undefined, undefined, - `data flow backfill : ${captureName} : disable : someKeyGoesHere` + `data flow backfill : ${captureName} : disable : ${initUUID}` ); if (updateResponse.error) { diff --git a/src/components/shared/Entity/prompts/steps/dataFlowReset/EnableCapture/index.tsx b/src/components/shared/Entity/prompts/steps/dataFlowReset/EnableCapture/index.tsx index 2e729c562..88833cdaf 100644 --- a/src/components/shared/Entity/prompts/steps/dataFlowReset/EnableCapture/index.tsx +++ b/src/components/shared/Entity/prompts/steps/dataFlowReset/EnableCapture/index.tsx @@ -3,19 +3,18 @@ import { createDraftSpec } from 'api/draftSpecs'; import { ProgressStates } from 'components/tables/RowActions/Shared/types'; import { useLoopIndex } from 'context/LoopIndex/useLoopIndex'; import { useMount } from 'react-use'; +import { generateDisabledSpec } from 'utils/entity-utils'; import { usePreSavePromptStore } from '../../../store/usePreSavePromptStore'; function EnableCapture() { const stepIndex = useLoopIndex(); const thisStep = usePreSavePromptStore((state) => state.steps[stepIndex]); - const [updateStep, updateContext, nextStep, context] = - usePreSavePromptStore((state) => [ - state.updateStep, - state.updateContext, - state.nextStep, - state.context, - ]); + const [updateStep, nextStep, context] = usePreSavePromptStore((state) => [ + state.updateStep, + state.nextStep, + state.context, + ]); useMount(() => { if (thisStep.state.progress === ProgressStates.IDLE) { @@ -28,22 +27,11 @@ function EnableCapture() { const updateResponse = await createDraftSpec( context.backfilledDraftId, context.captureName, - context.captureSpec, + generateDisabledSpec(context.captureSpec, true, false), 'capture', undefined, false ); - // const updateResponse = await modifyDraftSpec( - // generateDisabledSpec(context.captureSpec, true, false), - // { - // draft_id: context.backfilledDraftId, - // catalog_name: context.captureName, - // spec_type: 'capture', - // }, - // undefined, - // undefined, - // `data flow backfill : ${context.captureName} : enable : someKeyGoesHere` - // ); if (updateResponse.error) { updateStep(stepIndex, { @@ -53,8 +41,6 @@ function EnableCapture() { return; } - console.log('hey we are ready to publish', updateContext); - updateStep(stepIndex, { progress: ProgressStates.SUCCESS, valid: true, diff --git a/src/components/shared/Entity/prompts/steps/dataFlowReset/UpdateMaterialization/index.tsx b/src/components/shared/Entity/prompts/steps/dataFlowReset/UpdateMaterialization/index.tsx index 72eccbc17..354b98f13 100644 --- a/src/components/shared/Entity/prompts/steps/dataFlowReset/UpdateMaterialization/index.tsx +++ b/src/components/shared/Entity/prompts/steps/dataFlowReset/UpdateMaterialization/index.tsx @@ -18,12 +18,13 @@ function MarkMaterialization() { const collectionsBeingBackfilled = useBinding_collectionsBeingBackfilled(); - const [updateStep, updateContext, nextStep, context] = + const [updateStep, updateContext, nextStep, context, initUUID] = usePreSavePromptStore((state) => [ state.updateStep, state.updateContext, state.nextStep, state.context, + state.initUUID, ]); useMount(() => { @@ -46,7 +47,7 @@ function MarkMaterialization() { } const draftsResponse = await createEntityDraft( - `data flow backfill : ${context.backfillTarget.catalog_name} : create : someKeyGoesHere` + `data flow backfill : ${context.backfillTarget.catalog_name} : create : ${initUUID}` ); const backfilledDraftId = draftsResponse.data?.[0].id; @@ -77,6 +78,7 @@ function MarkMaterialization() { } }); + // Add to the draft const draftedMaterialization = await createDraftSpec( backfilledDraftId, context.backfillTarget.catalog_name, @@ -85,17 +87,6 @@ function MarkMaterialization() { undefined, false ); - // const draftedMaterialization = await modifyDraftSpec( - // updatedSpec, - // { - // catalog_name: context.backfillTarget.catalog_name, - // draft_id: backfilledDraftId, - // spec_type: 'materialization', - // }, - // undefined, - // undefined, - // `data flow backfill : ${context.backfillTarget.catalog_name} : update : someKeyGoesHere` - // ); if (draftedMaterialization.error) { updateStep(stepIndex, { diff --git a/src/components/shared/Entity/prompts/steps/preSave/Publish/index.tsx b/src/components/shared/Entity/prompts/steps/preSave/Publish/index.tsx index b9184fd60..0889a68ea 100644 --- a/src/components/shared/Entity/prompts/steps/preSave/Publish/index.tsx +++ b/src/components/shared/Entity/prompts/steps/preSave/Publish/index.tsx @@ -7,6 +7,7 @@ import { ProgressStates } from 'components/tables/RowActions/Shared/types'; import { useLoopIndex } from 'context/LoopIndex/useLoopIndex'; import useJobStatusPoller from 'hooks/useJobStatusPoller'; import { useMount } from 'react-use'; +import { useDetailsFormStore } from 'stores/DetailsForm/Store'; import { usePreSavePromptStore } from '../../../store/usePreSavePromptStore'; function Publish() { @@ -15,8 +16,17 @@ function Publish() { const stepIndex = useLoopIndex(); const thisStep = usePreSavePromptStore((state) => state.steps[stepIndex]); - const [updateStep, context, updateContext] = usePreSavePromptStore( - (state) => [state.updateStep, state.context, state.updateContext] + const [updateStep, context, updateContext, initUUID] = + usePreSavePromptStore((state) => [ + state.updateStep, + state.context, + state.updateContext, + state.initUUID, + ]); + + // TODO (data flow reset) need to plumb this through correctly + const dataPlaneName = useDetailsFormStore( + (state) => state.details.data.dataPlane?.dataPlaneName ); useMount(() => { @@ -29,7 +39,9 @@ function Publish() { // Start publishing it const publishResponse = await createPublication( context.backfilledDraftId, - false + false, + `data flow backfill : ${context.backfillTarget.catalog_name} : publish : ${initUUID}`, + dataPlaneName?.whole ); if (publishResponse.error || !publishResponse.data) { @@ -50,8 +62,6 @@ function Publish() { updateStep(stepIndex, { publicationStatus: successResponse, }); - - // nextStep(); }, async ( failedResponse: any //PublicationJobStatus | PostgrestError @@ -64,7 +74,6 @@ function Publish() { progress: ProgressStates.FAILED, valid: false, }); - // logRocketEvent(CustomEvents.REPUBLISH_PREFIX_FAILED); } ); }; diff --git a/src/components/shared/Entity/prompts/store/types.ts b/src/components/shared/Entity/prompts/store/types.ts index 52506eda1..c6884f8b8 100644 --- a/src/components/shared/Entity/prompts/store/types.ts +++ b/src/components/shared/Entity/prompts/store/types.ts @@ -10,6 +10,7 @@ export interface PreSavePromptStore<T = any> { updateStep: (step: number, settings: Partial<PromptStepState>) => void; initializeSteps: (backfillEnabled: boolean) => void; + initUUID: string | null; activeStep: number; setActiveStep: (val: PreSavePromptStore['activeStep']) => void; diff --git a/src/components/shared/Entity/prompts/store/usePreSavePromptStore.ts b/src/components/shared/Entity/prompts/store/usePreSavePromptStore.ts index 07a2ecb2d..86a0a0776 100644 --- a/src/components/shared/Entity/prompts/store/usePreSavePromptStore.ts +++ b/src/components/shared/Entity/prompts/store/usePreSavePromptStore.ts @@ -13,9 +13,10 @@ import { PreSavePromptStore } from './types'; const getInitialState = (): Pick< PreSavePromptStore, - 'activeStep' | 'steps' | 'context' + 'activeStep' | 'steps' | 'context' | 'initUUID' > => ({ activeStep: 0, + initUUID: null, steps: [], context: {}, }); @@ -38,6 +39,7 @@ export const usePreSavePromptStore = create<PreSavePromptStore>()( newSteps.push(PublishStep); state.steps = newSteps; + state.initUUID = crypto.randomUUID(); }), false, 'initializeSteps' From f1abfca8cdb37311c118760ea66c3fdc5beadcc6 Mon Sep 17 00:00:00 2001 From: Travis Jenkins <travis@estuary.dev> Date: Tue, 24 Sep 2024 22:02:36 -0400 Subject: [PATCH 49/66] Adding a LR event to try to make these sessions easier to find --- .../shared/Entity/prompts/store/usePreSavePromptStore.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/components/shared/Entity/prompts/store/usePreSavePromptStore.ts b/src/components/shared/Entity/prompts/store/usePreSavePromptStore.ts index 86a0a0776..55344adab 100644 --- a/src/components/shared/Entity/prompts/store/usePreSavePromptStore.ts +++ b/src/components/shared/Entity/prompts/store/usePreSavePromptStore.ts @@ -5,6 +5,8 @@ import { devtoolsOptions } from 'utils/store-utils'; import { useShallow } from 'zustand/react/shallow'; import { ProgressStates } from 'components/tables/RowActions/Shared/types'; import { JOB_STATUS_FAILURE, JOB_STATUS_SUCCESS } from 'services/supabase'; +import { logRocketEvent } from 'services/shared'; +import { CustomEvents } from 'services/types'; import { PromptStep } from '../types'; import { DataFlowResetSteps } from '../steps/dataFlowReset/shared'; import { ChangeReviewStep } from '../steps/preSave/ChangeReview/definition'; @@ -30,6 +32,7 @@ export const usePreSavePromptStore = create<PreSavePromptStore>()( initializeSteps: (backfillEnabled) => set( produce((state: PreSavePromptStore) => { + const initUUID = crypto.randomUUID(); const newSteps: PromptStep[] = [ChangeReviewStep]; if (backfillEnabled) { @@ -39,7 +42,11 @@ export const usePreSavePromptStore = create<PreSavePromptStore>()( newSteps.push(PublishStep); state.steps = newSteps; - state.initUUID = crypto.randomUUID(); + state.initUUID = initUUID; + logRocketEvent(CustomEvents.BACKFILL_DATAFLOW, { + initializedSteps: true, + initUUID, + }); }), false, 'initializeSteps' From 01b70993a48ad1131955512c73056b9b2dd8b44f Mon Sep 17 00:00:00 2001 From: Travis Jenkins <travis@estuary.dev> Date: Tue, 24 Sep 2024 22:06:46 -0400 Subject: [PATCH 50/66] Cleaning up comments Adding event to store to make it consistent --- .../prompts/steps/dataFlowReset/EnableCapture/index.tsx | 4 +--- .../shared/Entity/prompts/store/usePreSavePromptStore.ts | 7 ++++++- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/components/shared/Entity/prompts/steps/dataFlowReset/EnableCapture/index.tsx b/src/components/shared/Entity/prompts/steps/dataFlowReset/EnableCapture/index.tsx index 88833cdaf..d7cefbc10 100644 --- a/src/components/shared/Entity/prompts/steps/dataFlowReset/EnableCapture/index.tsx +++ b/src/components/shared/Entity/prompts/steps/dataFlowReset/EnableCapture/index.tsx @@ -45,10 +45,8 @@ function EnableCapture() { progress: ProgressStates.SUCCESS, valid: true, }); - nextStep(); - // // Start publishing it - // This needs to be the next step + nextStep(); }; void enableCapture(); diff --git a/src/components/shared/Entity/prompts/store/usePreSavePromptStore.ts b/src/components/shared/Entity/prompts/store/usePreSavePromptStore.ts index 55344adab..48bac0997 100644 --- a/src/components/shared/Entity/prompts/store/usePreSavePromptStore.ts +++ b/src/components/shared/Entity/prompts/store/usePreSavePromptStore.ts @@ -44,7 +44,7 @@ export const usePreSavePromptStore = create<PreSavePromptStore>()( state.steps = newSteps; state.initUUID = initUUID; logRocketEvent(CustomEvents.BACKFILL_DATAFLOW, { - initializedSteps: true, + step: 'init', initUUID, }); }), @@ -79,6 +79,11 @@ export const usePreSavePromptStore = create<PreSavePromptStore>()( }; } } + + logRocketEvent(CustomEvents.BACKFILL_DATAFLOW, { + step: updating.StepComponent.name, + progress: updating.state.progress, + }); }), false, 'setActiveStep' From 9c4c1288aa54a0cd7ff948d3f704cd6070c5696a Mon Sep 17 00:00:00 2001 From: Travis Jenkins <travis@estuary.dev> Date: Tue, 24 Sep 2024 22:14:55 -0400 Subject: [PATCH 51/66] Cleaning up comments --- .../shared/Entity/prompts/store/usePreSavePromptStore.ts | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/components/shared/Entity/prompts/store/usePreSavePromptStore.ts b/src/components/shared/Entity/prompts/store/usePreSavePromptStore.ts index 48bac0997..0499fa478 100644 --- a/src/components/shared/Entity/prompts/store/usePreSavePromptStore.ts +++ b/src/components/shared/Entity/prompts/store/usePreSavePromptStore.ts @@ -128,11 +128,6 @@ export const usePreSavePromptStore = create<PreSavePromptStore>()( produce((state: PreSavePromptStore) => { const newVal = state.activeStep - 1; state.activeStep = newVal >= 0 ? newVal : 0; - - // TODO - why did this not work? - // if (state.activeStep === 0) { - // state.setShow(false); - // } }), false, 'previousStep' From e64c7d3972dd6f491f4ce3fe5be9cfd770df4509 Mon Sep 17 00:00:00 2001 From: Travis Jenkins <travis@estuary.dev> Date: Tue, 24 Sep 2024 22:49:32 -0400 Subject: [PATCH 52/66] Adding some handling for draft errors --- .../Entity/prompts/PreSave/Content/index.tsx | 110 +++++++++++------- .../prompts/steps/preSave/Publish/index.tsx | 5 - src/lang/en-US/Workflows.ts | 3 + 3 files changed, 70 insertions(+), 48 deletions(-) diff --git a/src/components/shared/Entity/prompts/PreSave/Content/index.tsx b/src/components/shared/Entity/prompts/PreSave/Content/index.tsx index 690bab316..8f3568eff 100644 --- a/src/components/shared/Entity/prompts/PreSave/Content/index.tsx +++ b/src/components/shared/Entity/prompts/PreSave/Content/index.tsx @@ -2,10 +2,12 @@ import { Box, DialogContent, LinearProgress, + Stack, Step, StepContent, StepLabel, Stepper, + Typography, } from '@mui/material'; import ErrorBoundryWrapper from 'components/shared/ErrorBoundryWrapper'; import { @@ -17,15 +19,19 @@ import { useIntl } from 'react-intl'; import Error from 'components/shared/Error'; import ErrorLogs from 'components/shared/Entity/Error/Logs'; import { LoopIndexContextProvider } from 'context/LoopIndex'; -import { XmarkCircle } from 'iconoir-react'; +import DraftErrors from 'components/shared/Entity/Error/DraftErrors'; +import AlertBox from 'components/shared/AlertBox'; import { usePreSavePromptStore } from '../../store/usePreSavePromptStore'; function Content() { const intl = useIntl(); - const [activeStep, steps] = usePreSavePromptStore((state) => [ - state.activeStep, - state.steps, - ]); + const [activeStep, steps, backfilledDraftId] = usePreSavePromptStore( + (state) => [ + state.activeStep, + state.steps, + state.context?.backfilledDraftId, + ] + ); const renderedSteps = useMemo( () => @@ -46,16 +52,7 @@ function Content() { key={`PreSave-step-${stepLabelMessageId}-${index}`} completed={stepCompleted} > - <StepLabel - error={hasError} - StepIconProps={ - hasError - ? { - icon: <XmarkCircle />, - } - : undefined - } - > + <StepLabel error={hasError}> {intl.formatMessage({ id: stepLabelMessageId, })} @@ -63,37 +60,64 @@ function Content() { <StepContent> <ErrorBoundryWrapper> <LoopIndexContextProvider value={index}> - {progress === ProgressStates.RUNNING ? ( - <LinearProgress /> - ) : null} + <Stack spacing={2}> + {progress === + ProgressStates.RUNNING ? ( + <LinearProgress /> + ) : null} - {progress === ProgressStates.FAILED ? ( - <> - <Error + {backfilledDraftId ? ( + <AlertBox + short + hideIcon severity="error" - error={error} - condensed - /> - <Box> - <ErrorLogs - logToken={ - hasError - ? publicationStatus?.logs_token - : null + title={intl.formatMessage({ + id: 'preSavePrompt.draftErrors.title', + })} + > + <Typography> + {intl.formatMessage({ + id: 'preSavePrompt.draftErrors.message', + })} + </Typography> + + <DraftErrors + draftId={ + backfilledDraftId } - logProps={{ - fetchAll: true, - spinnerMessages: { - runningKey: - 'logs.default', - stoppedKey: - 'logs.noLogs', - }, - }} /> - </Box> - </> - ) : null} + </AlertBox> + ) : null} + + {progress === + ProgressStates.FAILED ? ( + <Stack spacing={2}> + <Error + severity="error" + error={error} + condensed + /> + <Box> + <ErrorLogs + logToken={ + hasError + ? publicationStatus?.logs_token + : null + } + logProps={{ + spinnerMessages: + { + runningKey: + 'logs.default', + stoppedKey: + 'logs.noLogs', + }, + }} + /> + </Box> + </Stack> + ) : null} + </Stack> <StepComponent /> </LoopIndexContextProvider> @@ -103,7 +127,7 @@ function Content() { ); } ), - [intl, steps] + [backfilledDraftId, intl, steps] ); return ( diff --git a/src/components/shared/Entity/prompts/steps/preSave/Publish/index.tsx b/src/components/shared/Entity/prompts/steps/preSave/Publish/index.tsx index 0889a68ea..fa13ff206 100644 --- a/src/components/shared/Entity/prompts/steps/preSave/Publish/index.tsx +++ b/src/components/shared/Entity/prompts/steps/preSave/Publish/index.tsx @@ -86,11 +86,6 @@ function Publish() { // eslint-disable-next-line react/jsx-no-useless-fragment return <></>; - // <LogDialogContent - // spinnerMessageId="preSavePrompt.logs.spinner" - // severity="info" - // token={logsToken} - // /> } export default Publish; diff --git a/src/lang/en-US/Workflows.ts b/src/lang/en-US/Workflows.ts index 3eef196d1..491e3fcba 100644 --- a/src/lang/en-US/Workflows.ts +++ b/src/lang/en-US/Workflows.ts @@ -133,6 +133,9 @@ export const Workflows: Record<string, string> = { 'preSavePrompt.logs.spinner.stopped': `done`, 'preSavePrompt.logs.spinner.running': `loading...`, + 'preSavePrompt.draftErrors.title': `Draft Errors`, + 'preSavePrompt.draftErrors.message': `There is an issue with the drafted version of your entity. Please contact support immediately.`, + 'dataFlowReset.selectMaterialization.title': `Select materialization for data flow reset`, 'dataFlowReset.reviewSelection.title': `Review your selections`, 'dataFlowReset.disableCapture.title': `Disable capture`, From e10ca70db9196dea1007bbe07f888b6cdabb071d Mon Sep 17 00:00:00 2001 From: Travis Jenkins <travis@estuary.dev> Date: Tue, 24 Sep 2024 23:14:39 -0400 Subject: [PATCH 53/66] Wiring up to exit the flow when complete Adding in a real time to prevent build from breaking Removing draft errors from alert --- src/components/shared/Entity/prompts/PreSave/Actions.tsx | 7 ++++++- .../shared/Entity/prompts/PreSave/Content/index.tsx | 9 ++++++--- .../steps/dataFlowReset/WaitForCaptureStop/index.tsx | 7 ++++++- .../shared/Entity/prompts/store/usePreSavePromptStore.ts | 2 +- 4 files changed, 19 insertions(+), 6 deletions(-) diff --git a/src/components/shared/Entity/prompts/PreSave/Actions.tsx b/src/components/shared/Entity/prompts/PreSave/Actions.tsx index 9f600dd7f..aa2c2bba5 100644 --- a/src/components/shared/Entity/prompts/PreSave/Actions.tsx +++ b/src/components/shared/Entity/prompts/PreSave/Actions.tsx @@ -1,9 +1,11 @@ import { Button, DialogActions, Stack } from '@mui/material'; import { useIntl } from 'react-intl'; import { useFormStateStore_setShowSavePrompt } from 'stores/FormState/hooks'; +import useEntityWorkflowHelpers from '../../hooks/useEntityWorkflowHelpers'; import { usePreSavePromptStore, usePreSavePromptStore_onFirstStep, + usePreSavePromptStore_onLastStep, usePreSavePromptStore_stepValid, } from '../store/usePreSavePromptStore'; @@ -18,6 +20,9 @@ function Actions() { const canContinue = usePreSavePromptStore_stepValid(); const onFirstStep = usePreSavePromptStore_onFirstStep(); + const onLastStep = usePreSavePromptStore_onLastStep(); + + const { exit } = useEntityWorkflowHelpers(); return ( <DialogActions> @@ -37,7 +42,7 @@ function Actions() { </Button> <Button - onClick={nextStep} + onClick={onLastStep ? () => exit() : nextStep} variant="outlined" disabled={!canContinue} > diff --git a/src/components/shared/Entity/prompts/PreSave/Content/index.tsx b/src/components/shared/Entity/prompts/PreSave/Content/index.tsx index 8f3568eff..01e4b56b6 100644 --- a/src/components/shared/Entity/prompts/PreSave/Content/index.tsx +++ b/src/components/shared/Entity/prompts/PreSave/Content/index.tsx @@ -7,7 +7,6 @@ import { StepContent, StepLabel, Stepper, - Typography, } from '@mui/material'; import ErrorBoundryWrapper from 'components/shared/ErrorBoundryWrapper'; import { @@ -20,7 +19,6 @@ import Error from 'components/shared/Error'; import ErrorLogs from 'components/shared/Entity/Error/Logs'; import { LoopIndexContextProvider } from 'context/LoopIndex'; import DraftErrors from 'components/shared/Entity/Error/DraftErrors'; -import AlertBox from 'components/shared/AlertBox'; import { usePreSavePromptStore } from '../../store/usePreSavePromptStore'; function Content() { @@ -67,6 +65,11 @@ function Content() { ) : null} {backfilledDraftId ? ( + <DraftErrors + draftId={backfilledDraftId} + /> + ) : null} + {/* TODO (data flow reset) <AlertBox short hideIcon @@ -87,7 +90,7 @@ function Content() { } /> </AlertBox> - ) : null} + */} {progress === ProgressStates.FAILED ? ( diff --git a/src/components/shared/Entity/prompts/steps/dataFlowReset/WaitForCaptureStop/index.tsx b/src/components/shared/Entity/prompts/steps/dataFlowReset/WaitForCaptureStop/index.tsx index d1e13bede..93564f208 100644 --- a/src/components/shared/Entity/prompts/steps/dataFlowReset/WaitForCaptureStop/index.tsx +++ b/src/components/shared/Entity/prompts/steps/dataFlowReset/WaitForCaptureStop/index.tsx @@ -3,6 +3,7 @@ import { useEditorStore_catalogName } from 'components/editor/Store/hooks'; import { ProgressStates } from 'components/tables/RowActions/Shared/types'; import { useLoopIndex } from 'context/LoopIndex/useLoopIndex'; import { useMount } from 'react-use'; +import { DateTime } from 'luxon'; import { usePreSavePromptStore } from '../../../store/usePreSavePromptStore'; function WaitForCaptureStop() { @@ -45,9 +46,13 @@ function WaitForCaptureStop() { // Start calling for shards // Loop over it until we see nothing is coming // Snag time + + const timeStopped = DateTime.utc().toFormat( + `yyyy-MM-dd'T'HH:mm:ss'Z'` + ); updateContext({ liveSpecId, - timeStopped: '01/01/2024', + timeStopped, }); // Fake timeout to make it feel more async diff --git a/src/components/shared/Entity/prompts/store/usePreSavePromptStore.ts b/src/components/shared/Entity/prompts/store/usePreSavePromptStore.ts index 0499fa478..9524ee776 100644 --- a/src/components/shared/Entity/prompts/store/usePreSavePromptStore.ts +++ b/src/components/shared/Entity/prompts/store/usePreSavePromptStore.ts @@ -161,7 +161,7 @@ export const usePreSavePromptStore_onFirstStep = () => { export const usePreSavePromptStore_onLastStep = () => { return usePreSavePromptStore( useShallow((state) => { - return state.activeStep === state.steps.length; + return state.activeStep === state.steps.length - 1; }) ); }; From 6b3396bef218846f7a5423dba464158b26d93aa6 Mon Sep 17 00:00:00 2001 From: Travis Jenkins <travis@estuary.dev> Date: Tue, 24 Sep 2024 23:15:58 -0400 Subject: [PATCH 54/66] Updating details message --- .../Entity/prompts/steps/dataFlowReset/DisableCapture/index.tsx | 2 +- .../prompts/steps/dataFlowReset/UpdateMaterialization/index.tsx | 2 +- .../shared/Entity/prompts/steps/preSave/Publish/index.tsx | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/shared/Entity/prompts/steps/dataFlowReset/DisableCapture/index.tsx b/src/components/shared/Entity/prompts/steps/dataFlowReset/DisableCapture/index.tsx index f3900c127..99061cee2 100644 --- a/src/components/shared/Entity/prompts/steps/dataFlowReset/DisableCapture/index.tsx +++ b/src/components/shared/Entity/prompts/steps/dataFlowReset/DisableCapture/index.tsx @@ -65,7 +65,7 @@ function DisableCapture() { }, undefined, undefined, - `data flow backfill : ${captureName} : disable : ${initUUID}` + `data flow backfill : disable : ${initUUID}` ); if (updateResponse.error) { diff --git a/src/components/shared/Entity/prompts/steps/dataFlowReset/UpdateMaterialization/index.tsx b/src/components/shared/Entity/prompts/steps/dataFlowReset/UpdateMaterialization/index.tsx index 354b98f13..bd14fee85 100644 --- a/src/components/shared/Entity/prompts/steps/dataFlowReset/UpdateMaterialization/index.tsx +++ b/src/components/shared/Entity/prompts/steps/dataFlowReset/UpdateMaterialization/index.tsx @@ -47,7 +47,7 @@ function MarkMaterialization() { } const draftsResponse = await createEntityDraft( - `data flow backfill : ${context.backfillTarget.catalog_name} : create : ${initUUID}` + `data flow backfill : update : ${initUUID}` ); const backfilledDraftId = draftsResponse.data?.[0].id; diff --git a/src/components/shared/Entity/prompts/steps/preSave/Publish/index.tsx b/src/components/shared/Entity/prompts/steps/preSave/Publish/index.tsx index fa13ff206..75867d502 100644 --- a/src/components/shared/Entity/prompts/steps/preSave/Publish/index.tsx +++ b/src/components/shared/Entity/prompts/steps/preSave/Publish/index.tsx @@ -40,7 +40,7 @@ function Publish() { const publishResponse = await createPublication( context.backfilledDraftId, false, - `data flow backfill : ${context.backfillTarget.catalog_name} : publish : ${initUUID}`, + `data flow backfill : publish : ${initUUID}`, dataPlaneName?.whole ); From 35ee6d8a66660c64528b543d9311baaabdb403b8 Mon Sep 17 00:00:00 2001 From: Travis Jenkins <travis@estuary.dev> Date: Tue, 24 Sep 2024 23:20:28 -0400 Subject: [PATCH 55/66] Disabling all the new stuff so this can be merged --- src/app/guards/EntityExistenceGuard.tsx | 5 +- .../editor/Bindings/Backfill/index.tsx | 7 +- src/components/shared/Entity/Actions/Save.tsx | 26 ++-- .../shared/Entity/CatalogEditor.tsx | 14 +- src/components/shared/Entity/Edit/index.tsx | 131 +++++++++--------- 5 files changed, 86 insertions(+), 97 deletions(-) diff --git a/src/app/guards/EntityExistenceGuard.tsx b/src/app/guards/EntityExistenceGuard.tsx index 48bb7506f..fc80f8be5 100644 --- a/src/app/guards/EntityExistenceGuard.tsx +++ b/src/app/guards/EntityExistenceGuard.tsx @@ -5,7 +5,6 @@ import useGlobalSearchParams, { } from 'hooks/searchParams/useGlobalSearchParams'; import { useLiveSpecsExtWithSpec } from 'hooks/useLiveSpecsExt'; import EntityNotFound from 'pages/error/EntityNotFound'; -import { useFormStateStore_setLiveSpec } from 'stores/FormState/hooks'; import { BaseComponentProps } from 'types'; function EntityExistenceGuard({ children }: BaseComponentProps) { @@ -14,7 +13,7 @@ function EntityExistenceGuard({ children }: BaseComponentProps) { const entityType = useEntityType(); // TODO (data flow reset) - const setLiveSpec = useFormStateStore_setLiveSpec(); + // const setLiveSpec = useFormStateStore_setLiveSpec(); const { liveSpecs, isValidating: checkingEntityExistence } = useLiveSpecsExtWithSpec(liveSpecId, entityType); @@ -25,7 +24,7 @@ function EntityExistenceGuard({ children }: BaseComponentProps) { return <EntityNotFound />; } else { // TODO (data flow reset) - setLiveSpec(liveSpecs[0].spec); + // setLiveSpec(liveSpecs[0].spec); // eslint-disable-next-line react/jsx-no-useless-fragment return <>{children}</>; diff --git a/src/components/editor/Bindings/Backfill/index.tsx b/src/components/editor/Bindings/Backfill/index.tsx index 733f7f233..1d08dff21 100644 --- a/src/components/editor/Bindings/Backfill/index.tsx +++ b/src/components/editor/Bindings/Backfill/index.tsx @@ -1,7 +1,6 @@ import { Box, Stack, Typography } from '@mui/material'; import BooleanToggleButton from 'components/shared/buttons/BooleanToggleButton'; import { BooleanString } from 'components/shared/buttons/types'; -import { useEntityWorkflow } from 'context/Workflow'; import { useCallback, useMemo } from 'react'; import { useIntl } from 'react-intl'; import { @@ -21,7 +20,6 @@ import { import { FormStatus } from 'stores/FormState/types'; import { useEditorStore_queryResponse_draftSpecs } from '../../Store/hooks'; import BackfillCount from './BackfillCount'; -import BackfillDataFlowOption from './BackfillDataFlowOption'; import BackfillNotSupportedAlert from './BackfillNotSupportedAlert'; import { BackfillProps } from './types'; import useUpdateBackfillCounter, { @@ -33,7 +31,7 @@ function Backfill({ description, bindingIndex = -1 }: BackfillProps) { const { updateBackfillCounter } = useUpdateBackfillCounter(); // TODO (data flow reset) - const workflow = useEntityWorkflow(); + // const workflow = useEntityWorkflow(); // Binding Store const currentCollection = useBinding_currentCollection(); @@ -201,10 +199,11 @@ function Backfill({ description, bindingIndex = -1 }: BackfillProps) { ) : null} </Stack> - {/*TODO (data flow reset)*/} + {/*TODO (data flow reset) {bindingIndex === -1 && workflow === 'capture_edit' ? ( <BackfillDataFlowOption /> ) : null} + */} </Box> ); } diff --git a/src/components/shared/Entity/Actions/Save.tsx b/src/components/shared/Entity/Actions/Save.tsx index 292ea16c0..efa99406f 100644 --- a/src/components/shared/Entity/Actions/Save.tsx +++ b/src/components/shared/Entity/Actions/Save.tsx @@ -5,13 +5,7 @@ import { } from 'components/editor/Store/hooks'; import { buttonSx } from 'components/shared/Entity/Header'; import { useIntl } from 'react-intl'; -import { useBinding_backfilledBindings_count } from 'stores/Binding/hooks'; -import { useBindingStore } from 'stores/Binding/Store'; - -import { - useFormStateStore_isActive, - useFormStateStore_setShowSavePrompt, -} from 'stores/FormState/hooks'; +import { useFormStateStore_isActive } from 'stores/FormState/hooks'; import { EntityCreateSaveButtonProps } from './types'; import useSave from './useSave'; @@ -31,11 +25,11 @@ function EntityCreateSave({ const draftId = useEditorStore_id(); const formActive = useFormStateStore_isActive(); - const setShowSavePrompt = useFormStateStore_setShowSavePrompt(); // TODO (data flow reset) - const backfillDataflow = useBindingStore((state) => state.backfillDataFlow); - const needsBackfilled = useBinding_backfilledBindings_count(); + // const setShowSavePrompt = useFormStateStore_setShowSavePrompt(); + // const backfillDataflow = useBindingStore((state) => state.backfillDataFlow); + // const needsBackfilled = useBinding_backfilledBindings_count(); return ( <Button @@ -43,13 +37,13 @@ function EntityCreateSave({ sx={buttonSx} onClick={async () => { // TODO (data flow reset) - if (!dryRun && backfillDataflow && needsBackfilled) { - setShowSavePrompt(true); - } else { - await save(draftId); - } + // if (!dryRun && backfillDataflow && needsBackfilled) { + // setShowSavePrompt(true); + // } else { + // await save(draftId); + // } - // await save(draftId); + await save(draftId); }} > {intl.formatMessage({ diff --git a/src/components/shared/Entity/CatalogEditor.tsx b/src/components/shared/Entity/CatalogEditor.tsx index 1ad8d4123..cfa3ce457 100644 --- a/src/components/shared/Entity/CatalogEditor.tsx +++ b/src/components/shared/Entity/CatalogEditor.tsx @@ -2,15 +2,12 @@ import { Paper, Typography } from '@mui/material'; import DraftSpecEditor from 'components/editor/DraftSpec'; import { useEditorStore_id } from 'components/editor/Store/hooks'; import WrapperWithHeader from 'components/shared/Entity/WrapperWithHeader'; -import { FormattedMessage, useIntl } from 'react-intl'; -import { useBinding_backfilledBindings_count } from 'stores/Binding/hooks'; -import { useBindingStore } from 'stores/Binding/Store'; +import { FormattedMessage } from 'react-intl'; import { useFormStateStore_isActive, useFormStateStore_status, } from 'stores/FormState/hooks'; import { FormStatus } from 'stores/FormState/types'; -import AlertBox from '../AlertBox'; import ErrorBoundryWrapper from '../ErrorBoundryWrapper'; interface Props { @@ -24,9 +21,9 @@ function CatalogEditor({ messageId }: Props) { const formActive = useFormStateStore_isActive(); // TODO (data flow reset) - const intl = useIntl(); - const backfillDataFlow = useBindingStore((state) => state.backfillDataFlow); - const needsBackfilled = useBinding_backfilledBindings_count(); + // const intl = useIntl(); + // const backfillDataFlow = useBindingStore((state) => state.backfillDataFlow); + // const needsBackfilled = useBinding_backfilledBindings_count(); if (draftId && formStatus !== FormStatus.INIT) { return ( @@ -44,7 +41,7 @@ function CatalogEditor({ messageId }: Props) { <FormattedMessage id={messageId} /> </Typography> - {/*TODO (data flow reset) - also make sure editor is disabled*/} + {/*TODO (data flow reset) - also make sure editor is disabled {backfillDataFlow && needsBackfilled ? ( <AlertBox fitWidth @@ -59,6 +56,7 @@ function CatalogEditor({ messageId }: Props) { })} </AlertBox> ) : null} + */} <Paper variant="outlined" sx={{ p: 1 }}> <DraftSpecEditor diff --git a/src/components/shared/Entity/Edit/index.tsx b/src/components/shared/Entity/Edit/index.tsx index b01ddf97f..594df46d9 100644 --- a/src/components/shared/Entity/Edit/index.tsx +++ b/src/components/shared/Entity/Edit/index.tsx @@ -38,8 +38,6 @@ import AlertBox from '../../AlertBox'; import IncompatibleCollections from '../IncompatibleCollections'; import ValidationErrorSummary from '../ValidationErrorSummary'; import { useFormHydrationChecker } from '../hooks/useFormHydrationChecker'; -import PreSavePrompt from '../prompts/PreSave'; -import PromptsHydrator from '../prompts/store/Hydrator'; interface Props { title: string; @@ -149,78 +147,79 @@ function EntityEdit({ entityType={entityType} entityName={entityName} > + {/*TODO (data flow reset) <PromptsHydrator> - <Collapse in={formSubmitError !== null}> - {formSubmitError ? ( - <EntityError - title={formSubmitError.title} - error={formSubmitError.error} - logToken={logToken} - draftId={persistedDraftId} - /> - ) : null} - </Collapse> - - <IncompatibleCollections /> - - {draftInitializationError ? ( - <Box sx={{ mb: 2 }}> - <AlertBox - severity={draftInitializationError.severity} - > - <FormattedMessage - id={draftInitializationError.messageId} - /> - </AlertBox> - </Box> + */} + <Collapse in={formSubmitError !== null}> + {formSubmitError ? ( + <EntityError + title={formSubmitError.title} + error={formSubmitError.error} + logToken={logToken} + draftId={persistedDraftId} + /> ) : null} + </Collapse> + + <IncompatibleCollections /> - {!isValidating && connectorTags.length === 0 ? ( - <AlertBox severity="warning" short> + {draftInitializationError ? ( + <Box sx={{ mb: 2 }}> + <AlertBox + severity={draftInitializationError.severity} + > <FormattedMessage - id={`${messagePrefix}.missingConnectors`} + id={draftInitializationError.messageId} /> </AlertBox> - ) : connectorTags.length > 0 ? ( - <ErrorBoundryWrapper> - <DetailsForm - connectorTags={connectorTags} - readOnly={readOnly.detailsForm} - entityType={entityType} - /> - </ErrorBoundryWrapper> - ) : null} - - {imageTag.connectorId ? ( - <ErrorBoundryWrapper> - <EndpointConfig - connectorImage={imageTag.id} - readOnly={readOnly.endpointConfigForm} - hideBorder={ - !hasLength(imageTag.connectorId) - } - /> - </ErrorBoundryWrapper> - ) : null} - - {hasLength(imageTag.connectorId) ? ( - <ErrorBoundryWrapper> - <CollectionConfig - draftSpecs={taskDraftSpec} - readOnly={readOnly.resourceConfigForm} - hideBorder={!draftId} - RediscoverButton={RediscoverButton} - /> - </ErrorBoundryWrapper> - ) : null} - - <CatalogEditor - messageId={`${messagePrefix}.finalReview.instructions`} - /> - - {/*TODO (data flow reset)*/} + </Box> + ) : null} + + {!isValidating && connectorTags.length === 0 ? ( + <AlertBox severity="warning" short> + <FormattedMessage + id={`${messagePrefix}.missingConnectors`} + /> + </AlertBox> + ) : connectorTags.length > 0 ? ( + <ErrorBoundryWrapper> + <DetailsForm + connectorTags={connectorTags} + readOnly={readOnly.detailsForm} + entityType={entityType} + /> + </ErrorBoundryWrapper> + ) : null} + + {imageTag.connectorId ? ( + <ErrorBoundryWrapper> + <EndpointConfig + connectorImage={imageTag.id} + readOnly={readOnly.endpointConfigForm} + hideBorder={!hasLength(imageTag.connectorId)} + /> + </ErrorBoundryWrapper> + ) : null} + + {hasLength(imageTag.connectorId) ? ( + <ErrorBoundryWrapper> + <CollectionConfig + draftSpecs={taskDraftSpec} + readOnly={readOnly.resourceConfigForm} + hideBorder={!draftId} + RediscoverButton={RediscoverButton} + /> + </ErrorBoundryWrapper> + ) : null} + + <CatalogEditor + messageId={`${messagePrefix}.finalReview.instructions`} + /> + + {/*TODO (data flow reset) <PreSavePrompt /> </PromptsHydrator> + */} </DraftSpecEditorHydrator> )} </> From a4a6de2e99d41103b0f6d3f2b8b3f56537514d59 Mon Sep 17 00:00:00 2001 From: Travis Jenkins <travis@estuary.dev> Date: Tue, 24 Sep 2024 23:28:28 -0400 Subject: [PATCH 56/66] Removing `detail` from the forms. --- src/api/liveSpecsExt.ts | 2 -- .../shared/Entity/Actions/useSave.ts | 6 +---- .../Entity/DetailsForm/useFormFields.ts | 23 ++----------------- src/stores/DetailsForm/Store.ts | 2 -- src/stores/DetailsForm/types.ts | 1 - 5 files changed, 3 insertions(+), 31 deletions(-) diff --git a/src/api/liveSpecsExt.ts b/src/api/liveSpecsExt.ts index 1858bfd36..1a4c3ebe5 100644 --- a/src/api/liveSpecsExt.ts +++ b/src/api/liveSpecsExt.ts @@ -206,7 +206,6 @@ export interface LiveSpecsExtQuery_DetailsForm { spec_type: Entity; spec: any; data_plane_id: string; - detail: string | null; connector_tag_id: string; connector_image_name: string; connector_image_tag: string; @@ -219,7 +218,6 @@ const DETAILS_FORM_QUERY = ` spec_type, spec, data_plane_id, - detail, connector_tag_id, connector_image_name, connector_image_tag, diff --git a/src/components/shared/Entity/Actions/useSave.ts b/src/components/shared/Entity/Actions/useSave.ts index b82b1f279..c510d3ece 100644 --- a/src/components/shared/Entity/Actions/useSave.ts +++ b/src/components/shared/Entity/Actions/useSave.ts @@ -60,9 +60,6 @@ function useSave( const setDiscoveredDraftId = useEditorStore_setDiscoveredDraftId(); const mutateDraftSpecs = useEditorStore_queryResponse_mutate(); - const entityDescription = useDetailsFormStore( - (state) => state.details.data.description - ); const dataPlaneName = useDetailsFormStore( (state) => state.details.data.dataPlane?.dataPlaneName ); @@ -263,7 +260,7 @@ function useSave( const response = await createPublication( draftId, dryRun ?? false, - entityDescription, + undefined, dataPlaneName?.whole ); if (response.error) { @@ -288,7 +285,6 @@ function useSave( dataPlaneName?.whole, disabledBindings, dryRun, - entityDescription, fullSourceErrorsExist, intl, messagePrefix, diff --git a/src/components/shared/Entity/DetailsForm/useFormFields.ts b/src/components/shared/Entity/DetailsForm/useFormFields.ts index d4357a6c7..99f4a202f 100644 --- a/src/components/shared/Entity/DetailsForm/useFormFields.ts +++ b/src/components/shared/Entity/DetailsForm/useFormFields.ts @@ -39,12 +39,6 @@ export default function useFormFields( const baseProperties = { [CATALOG_NAME_SCOPE]: { type: 'string' }, [CONNECTOR_IMAGE_SCOPE]: connectorSchema, - description: { - description: intl.formatMessage({ - id: 'description.description', - }), - type: 'string', - }, }; const baseRequirements = [CATALOG_NAME_SCOPE, CONNECTOR_IMAGE_SCOPE]; @@ -63,7 +57,7 @@ export default function useFormFields( required: baseRequirements, type: 'object', }; - }, [connectorSchema, dataPlaneError, dataPlaneSchema, intl]); + }, [connectorSchema, dataPlaneError, dataPlaneSchema]); const uiSchema = useMemo(() => { const catalogNameUISchema = { @@ -74,14 +68,6 @@ export default function useFormFields( type: 'Control', }; - const descriptionUISchema = { - label: intl.formatMessage({ - id: 'description.label', - }), - scope: '#/properties/description', - type: 'Control', - }; - return { elements: [ { @@ -91,13 +77,8 @@ export default function useFormFields( connectorUISchema, catalogNameUISchema, dataPlaneUISchema, - descriptionUISchema, ] - : [ - connectorUISchema, - catalogNameUISchema, - descriptionUISchema, - ], + : [connectorUISchema, catalogNameUISchema], type: 'HorizontalLayout', }, ], diff --git a/src/stores/DetailsForm/Store.ts b/src/stores/DetailsForm/Store.ts index b28f29d97..c9e92f86e 100644 --- a/src/stores/DetailsForm/Store.ts +++ b/src/stores/DetailsForm/Store.ts @@ -337,7 +337,6 @@ export const getInitialState = ( connector_image_tag, connector_tag_id, data_plane_id, - detail, } = data[0]; const connectorImage = await getConnectorImage( @@ -362,7 +361,6 @@ export const getInitialState = ( entityName: catalog_name, connectorImage, dataPlane, - description: detail ?? '', }, }; diff --git a/src/stores/DetailsForm/types.ts b/src/stores/DetailsForm/types.ts index c6fd0515d..9971d0fb1 100644 --- a/src/stores/DetailsForm/types.ts +++ b/src/stores/DetailsForm/types.ts @@ -28,7 +28,6 @@ export interface Details extends Pick<JsonFormsCore, 'data' | 'errors'> { }; entityName: string; dataPlane?: DataPlaneOption; - description?: string; }; } From bb93b110f855090db189b74dd7260455d74e85f6 Mon Sep 17 00:00:00 2001 From: Travis Jenkins <travis@estuary.dev> Date: Tue, 24 Sep 2024 23:32:05 -0400 Subject: [PATCH 57/66] Removing code that is not needed --- src/api/liveSpecsExt.ts | 10 ---------- src/hooks/useLiveSpecsExt.ts | 1 - 2 files changed, 11 deletions(-) diff --git a/src/api/liveSpecsExt.ts b/src/api/liveSpecsExt.ts index 1a4c3ebe5..07ef61b61 100644 --- a/src/api/liveSpecsExt.ts +++ b/src/api/liveSpecsExt.ts @@ -431,15 +431,6 @@ const getLiveSpecsRelatedToMaterialization = async ( .returns<LiveSpecsExt_Related[]>(); }; -const getLiveSpecsWithRelatedSourceCapture = async (captureName: string) => { - return supabaseClient - .from(TABLES.LIVE_SPECS_EXT) - .select(liveSpecsExtRelatedQuery) - .eq('spec_type', 'materialization') - .or(`spec->>sourceCapture.eq.${captureName}`) - .returns<LiveSpecsExt_Related[]>(); -}; - export { getLiveSpecs_captures, getLiveSpecs_collections, @@ -454,5 +445,4 @@ export { getLiveSpecShards, getLiveSpecSpec, getLiveSpecsRelatedToMaterialization, - getLiveSpecsWithRelatedSourceCapture, }; diff --git a/src/hooks/useLiveSpecsExt.ts b/src/hooks/useLiveSpecsExt.ts index 82883b180..0a3af6a45 100644 --- a/src/hooks/useLiveSpecsExt.ts +++ b/src/hooks/useLiveSpecsExt.ts @@ -123,7 +123,6 @@ export function useLiveSpecsExt_related(captureName: string) { .or(`spec->>sourceCapture.eq.${captureName}`) .returns<LiveSpecsExt_Related[]>() // getLiveSpecsRelatedToMaterialization(collectionName) - // getLiveSpecsWithRelatedSourceCapture(captureName) ); return { From 44efb6bfde7592b80ffdd698e3b2b5665c5cc133 Mon Sep 17 00:00:00 2001 From: Travis Jenkins <travis@estuary.dev> Date: Tue, 24 Sep 2024 23:32:43 -0400 Subject: [PATCH 58/66] PR: comments --- .../prompts/steps/dataFlowReset/SelectMaterialization/index.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/components/shared/Entity/prompts/steps/dataFlowReset/SelectMaterialization/index.tsx b/src/components/shared/Entity/prompts/steps/dataFlowReset/SelectMaterialization/index.tsx index 61ca085bb..ccc175679 100644 --- a/src/components/shared/Entity/prompts/steps/dataFlowReset/SelectMaterialization/index.tsx +++ b/src/components/shared/Entity/prompts/steps/dataFlowReset/SelectMaterialization/index.tsx @@ -10,6 +10,8 @@ function SelectMaterialization() { const draftSpecs = useEditorStore_queryResponse_draftSpecs(); + // TODO (data flow backfill) + // Go ahead and work this into the hydration of the store... I think const { related, error, isValidating } = useLiveSpecsExt_related( draftSpecs[0].catalog_name ); From ff37f8111767dc983db3296724bea0e448ad706a Mon Sep 17 00:00:00 2001 From: Travis Jenkins <travis@estuary.dev> Date: Tue, 24 Sep 2024 23:34:00 -0400 Subject: [PATCH 59/66] PR: commenting out function that isn't used --- src/api/liveSpecsExt.ts | 68 ++++++++++++++++++----------------------- 1 file changed, 30 insertions(+), 38 deletions(-) diff --git a/src/api/liveSpecsExt.ts b/src/api/liveSpecsExt.ts index 07ef61b61..509cf5ae3 100644 --- a/src/api/liveSpecsExt.ts +++ b/src/api/liveSpecsExt.ts @@ -393,43 +393,36 @@ export interface LiveSpecsExt_Related { reads_from: string[]; id: string; } -const getLiveSpecsRelatedToMaterialization = async ( - collectionNames: string[] -) => { - // const limiter = pLimit(3); - // const promises = []; - // let index = 0; - - // // TODO (retry) promise generator - // const promiseGenerator = (idx: number) => { - // return supabaseClient - // .from(TABLES.LIVE_SPECS_EXT) - // .select(liveSpecsExtRelatedQuery) - // .eq('spec_type', 'materialization') - // .overlaps( - // 'reads_from', - // collectionNames.slice(idx, idx + CHUNK_SIZE) - // ) - // .returns<LiveSpecsExt_Related[]>(); - // }; - - // while (index < collectionNames.length) { - // const prom = promiseGenerator(index); - // promises.push(limiter(() => prom)); - // index = index + CHUNK_SIZE; - // } - - // const response = await Promise.all(promises); - // const errors = response.filter((r) => r.error); - // return errors[0] ?? response[0]; - - return supabaseClient - .from(TABLES.LIVE_SPECS_EXT) - .select(liveSpecsExtRelatedQuery) - .eq('spec_type', 'materialization') - .overlaps('reads_from', collectionNames) - .returns<LiveSpecsExt_Related[]>(); -}; +// const getLiveSpecsRelatedToMaterialization = async ( +// collectionNames: string[] +// ) => { +// const limiter = pLimit(3); +// const promises = []; +// let index = 0; + +// // TODO (retry) promise generator +// const promiseGenerator = (idx: number) => { +// return supabaseClient +// .from(TABLES.LIVE_SPECS_EXT) +// .select(liveSpecsExtRelatedQuery) +// .eq('spec_type', 'materialization') +// .overlaps( +// 'reads_from', +// collectionNames.slice(idx, idx + CHUNK_SIZE) +// ) +// .returns<LiveSpecsExt_Related[]>(); +// }; + +// while (index < collectionNames.length) { +// const prom = promiseGenerator(index); +// promises.push(limiter(() => prom)); +// index = index + CHUNK_SIZE; +// } + +// const response = await Promise.all(promises); +// const errors = response.filter((r) => r.error); +// return errors[0] ?? response[0]; +// }; export { getLiveSpecs_captures, @@ -444,5 +437,4 @@ export { getLiveSpecsByLiveSpecId, getLiveSpecShards, getLiveSpecSpec, - getLiveSpecsRelatedToMaterialization, }; From 7e7e6fe36db9ae0a25b01a49f3104c1aa90e7148 Mon Sep 17 00:00:00 2001 From: Travis Jenkins <travis@estuary.dev> Date: Tue, 24 Sep 2024 23:46:07 -0400 Subject: [PATCH 60/66] PR: commenting out to disable this --- .../shared/Entity/hooks/useEntityWorkflowHelpers.ts | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/components/shared/Entity/hooks/useEntityWorkflowHelpers.ts b/src/components/shared/Entity/hooks/useEntityWorkflowHelpers.ts index 4b533a6c1..a8b4a1d80 100644 --- a/src/components/shared/Entity/hooks/useEntityWorkflowHelpers.ts +++ b/src/components/shared/Entity/hooks/useEntityWorkflowHelpers.ts @@ -29,7 +29,6 @@ import { useSourceCaptureStore } from 'stores/SourceCapture/Store'; import { useTransformationCreate_resetState } from 'stores/TransformationCreate/hooks'; import { getPathWithParams } from 'utils/misc-utils'; import { snackbarSettings } from 'utils/notification-utils'; -import { usePreSavePromptStore } from '../prompts/store/usePreSavePromptStore'; function useEntityWorkflowHelpers() { const { enqueueSnackbar } = useSnackbar(); @@ -71,10 +70,11 @@ function useEntityWorkflowHelpers() { // Transformation Create Store const resetTransformationCreateState = useTransformationCreate_resetState(); + // TODO (data flow reset) // PreSave Prompt Store - const resetPreSavePrompt = usePreSavePromptStore( - (state) => state.resetState - ); + // const resetPreSavePrompt = usePreSavePromptStore( + // (state) => state.resetState + // ); const resetState = useCallback(() => { resetFormState(); @@ -86,7 +86,7 @@ function useEntityWorkflowHelpers() { resetSchemaEvolutionState(); resetSourceCapture(); resetTransformationCreateState(); - resetPreSavePrompt(); + // resetPreSavePrompt(); }, [ resetBindingState, resetBindingsEditorStore, @@ -97,7 +97,6 @@ function useEntityWorkflowHelpers() { resetSchemaEvolutionState, resetSourceCapture, resetTransformationCreateState, - resetPreSavePrompt, ]); const callFailed = useCallback( From e2efcb6d8f1c44dc3925cea3fe5e2693f4f16bf2 Mon Sep 17 00:00:00 2001 From: Travis Jenkins <travis@estuary.dev> Date: Wed, 25 Sep 2024 11:16:11 -0400 Subject: [PATCH 61/66] Making code a bit simpler Typing the ref Including the current top height when scrolling --- src/components/shared/ChipList/index.tsx | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/components/shared/ChipList/index.tsx b/src/components/shared/ChipList/index.tsx index 196276d1e..dfe6613e3 100644 --- a/src/components/shared/ChipList/index.tsx +++ b/src/components/shared/ChipList/index.tsx @@ -13,7 +13,7 @@ function ChipList({ }: ChipListProps) { const intl = useIntl(); - const listScroller = useRef<any>(null); + const listScroller = useRef<HTMLDivElement>(null); // Format data coming in so we can still pass in a list of strings const formattedValues = useMemo(() => { @@ -48,9 +48,11 @@ function ChipList({ // When all chips are shown scroll down just a hair to try to make // sure the user knows that the list is scrollable useEffect(() => { - if (maxRender === valueLength) { - listScroller.current?.scrollTo(undefined, 10); + if (!listScroller.current || maxRender !== valueLength) { + return; } + + listScroller.current.scrollTop += 20; }, [maxRender, valueLength]); return ( From a89a4fec25e300582afdc5e3ce04b617e29cc866 Mon Sep 17 00:00:00 2001 From: Travis Jenkins <travis@estuary.dev> Date: Wed, 25 Sep 2024 11:19:46 -0400 Subject: [PATCH 62/66] Getting the chips aligned --- src/components/shared/ChipList/index.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/shared/ChipList/index.tsx b/src/components/shared/ChipList/index.tsx index dfe6613e3..2587e48c7 100644 --- a/src/components/shared/ChipList/index.tsx +++ b/src/components/shared/ChipList/index.tsx @@ -62,6 +62,7 @@ function ChipList({ display: 'flex', flexWrap: 'wrap', listStyle: 'none', + alignItems: 'center', p: 0, m: 0, minWidth: 100, From e90156803130a67838ecb5e122ea300f990a9eb6 Mon Sep 17 00:00:00 2001 From: Travis Jenkins <travis@estuary.dev> Date: Wed, 25 Sep 2024 11:30:55 -0400 Subject: [PATCH 63/66] PR: putting original count back in --- src/components/editor/Bindings/Backfill/BackfillCount.tsx | 4 ++-- src/stores/Binding/hooks.ts | 3 +++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/components/editor/Bindings/Backfill/BackfillCount.tsx b/src/components/editor/Bindings/Backfill/BackfillCount.tsx index 1f1065d78..254d8514a 100644 --- a/src/components/editor/Bindings/Backfill/BackfillCount.tsx +++ b/src/components/editor/Bindings/Backfill/BackfillCount.tsx @@ -4,7 +4,7 @@ import { useMemo } from 'react'; import { useIntl } from 'react-intl'; import { useBinding_backfilledBindings_count, - useBinding_enabledCollections_count, + useBinding_collections_count, } from 'stores/Binding/hooks'; import { BackfillCountProps } from './types'; @@ -13,7 +13,7 @@ function BackfillCount({ disabled }: BackfillCountProps) { const entityType = useEntityType(); const backfillCount = useBinding_backfilledBindings_count(); - const bindingsTotal = useBinding_enabledCollections_count(); + const bindingsTotal = useBinding_collections_count(); // Only reason noBackfill is in here is because we are already running the memo on backfillCount change const [noBackfill, itemType_backfill, itemType_bindings] = useMemo(() => { diff --git a/src/stores/Binding/hooks.ts b/src/stores/Binding/hooks.ts index 30f615251..cc6ca61d9 100644 --- a/src/stores/Binding/hooks.ts +++ b/src/stores/Binding/hooks.ts @@ -122,6 +122,9 @@ export const useBinding_collections = () => { return useBindingStore(useShallow((state) => state.getCollections())); }; +export const useBinding_collections_count = () => + useBindingStore(useShallow((state) => state.getCollections().length)); + export const useBinding_enabledCollections_count = () => useBindingStore( useShallow((state) => state.getEnabledCollections().length) From c4e48f8ae2456b98cd90359fabe0fa7dbf847c63 Mon Sep 17 00:00:00 2001 From: Travis Jenkins <travis@estuary.dev> Date: Wed, 25 Sep 2024 11:38:46 -0400 Subject: [PATCH 64/66] Updating content to be more clear Switching out format components --- src/components/editor/Bindings/index.tsx | 8 +++----- src/lang/en-US/Workflows.ts | 4 ++-- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/components/editor/Bindings/index.tsx b/src/components/editor/Bindings/index.tsx index dd415d0b1..30399e778 100644 --- a/src/components/editor/Bindings/index.tsx +++ b/src/components/editor/Bindings/index.tsx @@ -110,11 +110,9 @@ function BindingsMultiEditor({ {workflow === 'capture_edit' || workflow === 'materialization_edit' ? ( <Backfill - description={ - <FormattedMessage - id={`workflows.collectionSelector.manualBackfill.message.${entityType}.allBindings`} - /> - } + description={intl.formatMessage({ + id: `workflows.collectionSelector.manualBackfill.message.${entityType}.allBindings`, + })} /> ) : null} </Stack> diff --git a/src/lang/en-US/Workflows.ts b/src/lang/en-US/Workflows.ts index 491e3fcba..a6bc4e065 100644 --- a/src/lang/en-US/Workflows.ts +++ b/src/lang/en-US/Workflows.ts @@ -77,9 +77,9 @@ export const Workflows: Record<string, string> = { 'workflows.collectionSelector.manualBackfill.notSupported.title': `This {entityType} doesn’t support backfills.`, 'workflows.collectionSelector.manualBackfill.notSupported.message': `To backfill, disable each binding, save and then re-enable and save.`, 'workflows.collectionSelector.manualBackfill.message.capture': `Trigger a backfill of this collection from the source when published.`, - 'workflows.collectionSelector.manualBackfill.message.capture.allBindings': `Trigger a backfill of all collections from the source when published.`, + 'workflows.collectionSelector.manualBackfill.message.capture.allBindings': `Trigger a backfill of all collection from the source when published. The UI will mark all collections as backfilled but the server will filter out those that cannot be backfilled (e.g. disabled collections).`, 'workflows.collectionSelector.manualBackfill.message.materialization': `Trigger a backfill from the source collection to its materialized resource when published.`, - 'workflows.collectionSelector.manualBackfill.message.materialization.allBindings': `Trigger a backfill from all source collections to their materialized resource when published.`, + 'workflows.collectionSelector.manualBackfill.message.materialization.allBindings': `Trigger a backfill from all source collections to their materialized resource when published. The UI will mark all collections as backfilled but the server will filter out those that cannot be backfilled (e.g. disabled collections).`, 'workflows.collectionSelector.manualBackfill.cta.backfill': `Backfill`, 'workflows.collectionSelector.manualBackfill.count': `{backfillCount} of {bindingsTotal} {itemType} marked for backfill`, 'workflows.collectionSelector.manualBackfill.count.empty': `no {itemType} marked for backfill`, From 6750662375073c48f13000da4a21e4ff0a6f37e9 Mon Sep 17 00:00:00 2001 From: Travis Jenkins <travis@estuary.dev> Date: Wed, 25 Sep 2024 11:54:58 -0400 Subject: [PATCH 65/66] PR: typo and small content change --- src/lang/en-US/Workflows.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lang/en-US/Workflows.ts b/src/lang/en-US/Workflows.ts index a6bc4e065..d1b187383 100644 --- a/src/lang/en-US/Workflows.ts +++ b/src/lang/en-US/Workflows.ts @@ -77,7 +77,7 @@ export const Workflows: Record<string, string> = { 'workflows.collectionSelector.manualBackfill.notSupported.title': `This {entityType} doesn’t support backfills.`, 'workflows.collectionSelector.manualBackfill.notSupported.message': `To backfill, disable each binding, save and then re-enable and save.`, 'workflows.collectionSelector.manualBackfill.message.capture': `Trigger a backfill of this collection from the source when published.`, - 'workflows.collectionSelector.manualBackfill.message.capture.allBindings': `Trigger a backfill of all collection from the source when published. The UI will mark all collections as backfilled but the server will filter out those that cannot be backfilled (e.g. disabled collections).`, + 'workflows.collectionSelector.manualBackfill.message.capture.allBindings': `Trigger a backfill of all collections from the source when published. The UI will mark all collections to be backfilled but the server will filter out those that cannot be backfilled (e.g. disabled collections).`, 'workflows.collectionSelector.manualBackfill.message.materialization': `Trigger a backfill from the source collection to its materialized resource when published.`, 'workflows.collectionSelector.manualBackfill.message.materialization.allBindings': `Trigger a backfill from all source collections to their materialized resource when published. The UI will mark all collections as backfilled but the server will filter out those that cannot be backfilled (e.g. disabled collections).`, 'workflows.collectionSelector.manualBackfill.cta.backfill': `Backfill`, From 8396d89caa0c1d5be5f4971f02abb705bfc4286d Mon Sep 17 00:00:00 2001 From: Travis Jenkins <travis@estuary.dev> Date: Wed, 25 Sep 2024 12:00:44 -0400 Subject: [PATCH 66/66] PR: content typo --- src/lang/en-US/Workflows.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lang/en-US/Workflows.ts b/src/lang/en-US/Workflows.ts index d1b187383..ba7a2888a 100644 --- a/src/lang/en-US/Workflows.ts +++ b/src/lang/en-US/Workflows.ts @@ -79,7 +79,7 @@ export const Workflows: Record<string, string> = { 'workflows.collectionSelector.manualBackfill.message.capture': `Trigger a backfill of this collection from the source when published.`, 'workflows.collectionSelector.manualBackfill.message.capture.allBindings': `Trigger a backfill of all collections from the source when published. The UI will mark all collections to be backfilled but the server will filter out those that cannot be backfilled (e.g. disabled collections).`, 'workflows.collectionSelector.manualBackfill.message.materialization': `Trigger a backfill from the source collection to its materialized resource when published.`, - 'workflows.collectionSelector.manualBackfill.message.materialization.allBindings': `Trigger a backfill from all source collections to their materialized resource when published. The UI will mark all collections as backfilled but the server will filter out those that cannot be backfilled (e.g. disabled collections).`, + 'workflows.collectionSelector.manualBackfill.message.materialization.allBindings': `Trigger a backfill from all source collections to their materialized resource when published. The UI will mark all collections to be backfilled but the server will filter out those that cannot be backfilled (e.g. disabled collections).`, 'workflows.collectionSelector.manualBackfill.cta.backfill': `Backfill`, 'workflows.collectionSelector.manualBackfill.count': `{backfillCount} of {bindingsTotal} {itemType} marked for backfill`, 'workflows.collectionSelector.manualBackfill.count.empty': `no {itemType} marked for backfill`,