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

Nv 1458 web abstract variable manager to not #2529

Conversation

ainouzgali
Copy link
Contributor

@ainouzgali ainouzgali commented Jan 19, 2023

What change does this PR introduce?

Show var manager in layout editor, allow to set defaultValue and required for layout vars. Apply layout vars defaults in send email.

Why was this change needed?

Other information (Screenshots)

Screen Shot 2023-01-19 at 11 36 42

Screen Shot 2023-01-19 at 11 37 00

Screen Shot 2023-01-19 at 11 53 24

Screen Shot 2023-01-19 at 11 53 58

…bstract-variable-manager-to-not

# Conflicts:
#	apps/web/src/pages/brand/tabs/LayoutEditor.tsx
…bstract-variable-manager-to-not

# Conflicts:
#	apps/web/src/pages/brand/tabs/LayoutEditor.tsx
…l-content-in-Trigger' into nv-1458-web-abstract-variable-manager-to-not

# Conflicts:
#	apps/web/src/pages/brand/tabs/LayoutEditor.tsx
@linear
Copy link

linear bot commented Jan 19, 2023

NV-1458 Web - abstract variable manager to not apply only to template steps.

Currently, our variable manager is built to watch only the variables inside the template editor form. As it is now needed in layouts - different context and separate form - we need to abstract it so it we would be able to reuse our var manager.

@@ -49,12 +38,25 @@ export class CompileEmailTemplateUsecase {
useNovuDefault = true;
}
}
const layoutVariables = layout?.variables || [];
const defaultPayload = verifyPayloadService.verifyPayload(layoutVariables, command.payload);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

So defaultValue defined for the layout variables will also apply when sending email/preview/test

@what-the-diff
Copy link

what-the-diff bot commented Jan 19, 2023

  • Added VerifyPayloadService to verify payload for events and content templates
  • Updated layouts editor with new variables handling logic
  • Added path and control props to VariableManager component
  • Changed the way of getting variableName, variableType in VariableComponent from useFormContext() hook to useWatch() hook
  • Updated formPrefix for all Controller components inside VariableComponent based on new path prop value (if it's not empty) or template prop value otherwise
  • Added a new modal for managing variables
  • Changed the layout editor to use react-hooks-form instead of formik
  • Removed some unused code and added tests for this component

@ainouzgali ainouzgali marked this pull request as ready for review January 19, 2023 09:47
Copy link
Contributor

@p-fernandez p-fernandez left a comment

Choose a reason for hiding this comment

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

Great work. Some considerations. Do you plan to do Cypress tests for this piece?

Comment on lines +155 to +169
private async loadTemplateContent(name: string) {
return new Promise<string>((resolve, reject) => {
let path = '/../../compile-template';
if (!process.env.E2E_RUNNER) {
path = '/src/app/content-templates/usecases/compile-template';
}
fs.readFile(`${__dirname}${path}/templates/${name}`, (err, content) => {
if (err) {
return reject(err);
}

return resolve(content.toString());
});
});
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Well done making it reusable.

Comment on lines +4 to +8
export const novuReservedVariableNames = ['body'];

export function isReservedVariableName(variableName: string) {
return novuReservedVariableNames.includes(variableName);
}
Copy link
Contributor

Choose a reason for hiding this comment

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

👏🏻


@Injectable()
export class CreateLayoutUseCase {
constructor(private layoutRepository: LayoutRepository) {}

async execute(command: CreateLayoutCommand) {
const entity = this.mapToEntity(command);
const variables = this.getExtractedVariables(command.variables as ITemplateVariable[], command.content);
Copy link
Contributor

Choose a reason for hiding this comment

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

Instead of applying it here you could directly apply it, inside of the mapToEntity. It can be a matter of preference: either we want it clearly independently like you did, or we want to hide it a bit inside of the method that maps all properties.

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 did that at first :) But it felt that the map function look more complicated and less clear. Also, he really didn't like the types😅

@@ -43,4 +47,27 @@ export class CreateLayoutUseCase {
isDeleted: layout.deleted,
};
}

private getExtractedVariables(variables: ITemplateVariable[], content: string): ITemplateVariable[] {
const contentService = new ContentService();
Copy link
Contributor

Choose a reason for hiding this comment

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

What's our internal take on the use of a service? Just asking to consider suggesting to add it to the constructor of the use case rather that instantiate it every time the method is being 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.

Yes, I was also wondering what will be better

Comment on lines +17 to +21
const variableTypeHumanize = {
String: 'Value',
Array: 'Array',
Boolean: 'Boolean',
}[variable.type];
Copy link
Contributor

Choose a reason for hiding this comment

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

I understand that you are trying to humanize the variable type. So just swap the order in the name would reads more natural I think: humanizedVariableType. Though you could also to refer to it as variableTypeLabel that might go more in line with being the text will be rendered like in an UI.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sure, its an old code, I just moved it.

export const VariableComponent = ({ index, template }: VariableComponentProps) => {
const { control, watch } = useFormContext();
export const VariableComponent = ({ index, template, control, path }: VariableComponentProps) => {
const formPrefix = path ? `${path}variables.${index}` : `${template}.variables.${index}`;
Copy link
Contributor

Choose a reason for hiding this comment

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

Just to double check that the intention is to create the formPrefix for the case where path exists as: ${path}variables.${index} and not ${path}.variables.${index}.

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. Because I want to allow thw option of path being ''.
I want to refactor all the hooks and components related to var manager, we can do it much better. But will probably do it in cool down

Comment on lines +18 to +24
interface ILayoutForm {
content: string;
name: string;
description: string;
isDefault: boolean;
variables: ITemplateVariable[];
}
Copy link
Contributor

Choose a reason for hiding this comment

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

You can generate the interface from Pick<LayoutDto, 'content' | 'name' | 'description' | 'isDefault' | 'variables'>.

Comment on lines +82 to +91
variables.forEach((vari) => {
if (!arrayFields.find((field) => field.name === vari.name)) {
arrayFields.push(vari);
}
});
arrayFields.forEach((vari, ind) => {
if (!variables.find((field) => field.name === vari.name)) {
delete arrayFields[ind];
}
});
Copy link
Contributor

Choose a reason for hiding this comment

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

I am struggling to understand this bit. I have a feeling that this could be done with a single loop. Maybe with arrayFields being a Set and using delete.

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, it is a replicated code. Currently the hook we have for it is very dependent on a specific form context (the steps template.
I thought so to a few months ago haha. Worked on it for hours, eventually realized why it is needed- I'll tell you the problem later when we have more time and I remind myself a bit.

Comment on lines +222 to +231
styles={{
modal: {
backgroundColor: theme.colorScheme === 'dark' ? colors.B15 : colors.white,
width: '90%',
},
body: {
paddingTop: '5px',
paddingInline: '8px',
},
}}
Copy link
Contributor

Choose a reason for hiding this comment

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

Should we use useStyles?

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'm not sure I like doing that for a very specific Modal. We already have a Modal in our design system. I would prefer to make that one more flexible to style changes. Will add it to backlog ❤️ Maybe a community member would like to help with thah.

@ainouzgali
Copy link
Contributor Author

ainouzgali commented Jan 19, 2023

Great work. Some considerations. Do you plan to do Cypress tests for this piece?

Definitely! So many tests! But because of the huge PR I thought it's better if I do it separately.

Thank you @p-fernandez for the thorough code review! I really appreciate it

ainouzgali and others added 3 commits January 19, 2023 14:52
…ement/VariablesManagement.tsx

Co-authored-by: Pablo Fernández <pablo@novu.co>
…l-content-in-Trigger' into nv-1458-web-abstract-variable-manager-to-not

# Conflicts:
#	apps/api/src/app/layouts/use-cases/create-layout/create-layout.use-case.ts
@ainouzgali ainouzgali merged commit aaca4c5 into NV-1475-Compile-layout-with-email-content-in-Trigger Jan 19, 2023
@ainouzgali ainouzgali deleted the nv-1458-web-abstract-variable-manager-to-not branch January 19, 2023 20:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants