Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(dashboard): workflow editor autosave #6718

Merged
merged 4 commits into from
Oct 21, 2024

Conversation

LetItRock
Copy link
Contributor

What changed? Why was the change needed?

Workflow Editor autosave functionality and sonner toast for successfully saved action.

Because there are a few issues with the BE implementation on the video you can see that I only added one step.

Screenshots

Screen.Recording.2024-10-17.at.23.05.22.mov

Copy link

linear bot commented Oct 17, 2024

Copy link

netlify bot commented Oct 17, 2024

Deploy Preview for novu-stg-vite-dashboard-poc ready!

Name Link
🔨 Latest commit 2b1ec25
🔍 Latest deploy log https://app.netlify.com/sites/novu-stg-vite-dashboard-poc/deploys/6716079dd26e54000879760c
😎 Deploy Preview https://deploy-preview-6718--novu-stg-vite-dashboard-poc.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify site configuration.

apps/dashboard/package.json Outdated Show resolved Hide resolved
apps/dashboard/src/hooks/use-form-autosave.ts Outdated Show resolved Hide resolved
@@ -41,17 +41,21 @@
"class-variance-authority": "^0.7.0",
"clsx": "^2.1.1",
"date-fns": "^4.1.0",
"debounce": "^2.2.0",
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the debounce library used to debounce save workflow requests

"tailwind-merge": "^2.4.0",
"tailwindcss-animate": "^1.0.7",
"use-deep-compare-effect": "^1.8.1",
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

used in the autosave hook, more details below

);
};

const Toaster = ({ ...props }: ToasterProps) => {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

autogenerated


type ToasterProps = React.ComponentProps<typeof Sonner>;

const SmallToast = ({ children, className, ...props }: React.HTMLAttributes<HTMLDivElement>) => {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

small sonner implementation shown in the workflow editor I will explain to you why later in the comments

name: z.string(),
type: z.nativeEnum(StepTypeEnum),
})
.passthrough()
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

passthrough is passing all the other fields from the persisted step apart from what is defined in the schema, for example stepUuid, controls, controlValues, etc. Otherwise during the save we will be missing these and the BE will treat it as a new step.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh so this just doesn't strip out the unspecified fields.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes

return;
}

updateWorkflow({ id: workflow._id, workflow: { ...workflow, ...data } as any });
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

to satisfy TS until the BE types are fixed

Comment on lines 67 to 83
const id = toast(
<SmallToast>
<RiProgress1Line className="size-6" />
<span className="text-sm">Saved</span>
</SmallToast>,
{
duration: 5000,
position: 'bottom-left',
unstyled: true,
classNames: {
toast: 'ml-10',
},
onAutoClose: () => {
changesSavedToastIdRef.current = undefined;
},
}
);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is how the sonner toast is used. Opened for the discussion.
Basically, in the Workflow Editor, we might want to show different types of toasts, like a small version when the workflow is saved or a bigger one when the step is updated. And I haven't seen a way of saying which variant you want to show, this can only be defined when Toaster is used. That's why the only solution I saw was to use unstyled property and provide the component to the toast function call.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have you checked this out? shadcn-ui/ui#2254 (comment)
We can probably create our own functions with predefined css here. Or just a function that is just toast() with a custom component and use that across the app. Unless we are not planning to reuse this specific one somewhere else.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ohh nice, I didn't see that... seems to be a better approach, I'll update it then 🙌

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ahh I thought you could create your custom function...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are two options:

  • create a variant on the Toaster
  • use unstyled prop and provide a custom component <--- this is what I did

I didn't go with the first approach as we might want to show different toast types on the same page.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll move the code into the custom function so that it will be more reusable

<span className="text-sm">Saved</span>
</SmallToast>,
{
duration: 5000,
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tbd

},
});

useFormAutoSave({
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

form autosave hook

Comment on lines 17 to 32
const watchedData = useWatch<T>({
control,
});

const debouncedSave = useCallback<() => void>(
debounce(() => {
handleSubmit(onSubmitRef.current)();
}, 1000),
[handleSubmit]
);

useDeepCompareEffect(() => {
if (formState.isDirty) {
debouncedSave();
}
}, [watchedData]);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Basically, we watch all the form data changes and compare it deeply with the previous form-watched data between re-renders. If the form data was changed and the form is dirty, we call debounced save, which calls the react hook form handleSubmit that triggers validation. If the data is valid, then the onSubmit will be called.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe additionally we can show the sonner when there are validation errors?

Copy link
Contributor

@desiprisg desiprisg left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice work! Just some minor comments/suggestions.

name: z.string(),
type: z.nativeEnum(StepTypeEnum),
})
.passthrough()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh so this just doesn't strip out the unspecified fields.

Comment on lines 67 to 83
const id = toast(
<SmallToast>
<RiProgress1Line className="size-6" />
<span className="text-sm">Saved</span>
</SmallToast>,
{
duration: 5000,
position: 'bottom-left',
unstyled: true,
classNames: {
toast: 'ml-10',
},
onAutoClose: () => {
changesSavedToastIdRef.current = undefined;
},
}
);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have you checked this out? shadcn-ui/ui#2254 (comment)
We can probably create our own functions with predefined css here. Or just a function that is just toast() with a custom component and use that across the app. Unless we are not planning to reuse this specific one somewhere else.

apps/dashboard/src/hooks/use-form-autosave.ts Outdated Show resolved Hide resolved
Base automatically changed from nv-4482-workflow-editor-add-in-app-step to next October 18, 2024 11:59
Copy link

pkg-pr-new bot commented Oct 18, 2024

Open in Stackblitz

novu

pnpm add https://pkg.pr.new/novuhq/novu@6718

@novu/client

pnpm add https://pkg.pr.new/novuhq/novu/@novu/client@6718

@novu/framework

pnpm add https://pkg.pr.new/novuhq/novu/@novu/framework@6718

@novu/headless

pnpm add https://pkg.pr.new/novuhq/novu/@novu/headless@6718

@novu/nest

pnpm add https://pkg.pr.new/novuhq/novu/@novu/nest@6718

@novu/nextjs

pnpm add https://pkg.pr.new/novuhq/novu/@novu/nextjs@6718

@novu/js

pnpm add https://pkg.pr.new/novuhq/novu/@novu/js@6718

@novu/notification-center

pnpm add https://pkg.pr.new/novuhq/novu/@novu/notification-center@6718

@novu/node

pnpm add https://pkg.pr.new/novuhq/novu/@novu/node@6718

@novu/providers

pnpm add https://pkg.pr.new/novuhq/novu/@novu/providers@6718

@novu/react-native

pnpm add https://pkg.pr.new/novuhq/novu/@novu/react-native@6718

@novu/react

pnpm add https://pkg.pr.new/novuhq/novu/@novu/react@6718

@novu/shared

pnpm add https://pkg.pr.new/novuhq/novu/@novu/shared@6718

@novu/stateless

pnpm add https://pkg.pr.new/novuhq/novu/@novu/stateless@6718

commit: 2b1ec25

@LetItRock LetItRock merged commit 13aa3f8 into next Oct 21, 2024
40 checks passed
@LetItRock LetItRock deleted the nv-4480-workflow-editor-autosave branch October 21, 2024 08:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants