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

Post request for service catalog item #547

Merged
merged 1 commit into from
Dec 3, 2024

Conversation

gosiexon-zen
Copy link
Contributor

@gosiexon-zen gosiexon-zen commented Nov 22, 2024

Description

Added a functionality to submit a service catalog item request. Added validation to the form. Reorganize the structure of ServiceCatalogItemPage.tsx so the submit button is with the corresponding form. We have fixed values for subject and description in the request and it is in English only. This is probably only for EAP.

GG-3862

Screenshots

Form with validation:
Screenshot 2024-11-22 at 16 47 45

Requested service view:
Screenshot 2024-11-22 at 16 55 34

Checklist

  • 📗 all commit messages follow the conventional commits standard
  • ⬅️ changes are compatible with RTL direction
  • ♿ Changes to the UI are tested for accessibility and compliant with WCAG 2.1.
  • 📝 changes are tested in Chrome, Firefox, Safari and Edge
  • 📱 changes are responsive and tested in mobile
  • 👍 PR is approved by @zendesk/vikings

@gosiexon-zen gosiexon-zen requested a review from a team as a code owner November 22, 2024 15:56
@gosiexon-zen
Copy link
Contributor Author

@Fredx87 we are nor showing and passing value for the priority field. We need to think if we should add this.

@Fredx87
Copy link
Contributor

Fredx87 commented Nov 25, 2024

@Fredx87 we are nor showing and passing value for the priority field. We need to think if we should add this.

Yeah, the same should apply to the Type field (and the linked due_at field). These fields need some different handling, like sending their values at the top level instead of inside the custom_fields array and probably other stuff. For the EAP we can focus on custom fields and add support for those fields later

import type { Field } from "../ticket-fields";
import type { ServiceCatalogItem } from "./data-types/ServiceCatalogItem";

export function useServiceFormSubmit(
Copy link
Contributor

Choose a reason for hiding this comment

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

This function is not a custom hook since it does not call any other hook inside, as explained in the deep dive here. We can just export an async submitServiceItemRequest function, and this will simplify thing since we can call it conditionally without been forced to pass undefined values

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ok I changed that

return;
}
const currentUserRequest = await fetch("/api/v2/users/me.json");
const currentUser = await currentUserRequest.json();
Copy link
Contributor

Choose a reason for hiding this comment

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

We need to check if currentUserRequest is ok first

Comment on lines 16 to 23
const customFields = requestFields.map((field) => {
if (field.type !== "subject" && field.type !== "description") {
return {
id: field.id,
value: field.value,
};
} else return;
});
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: at this point, it is probably better to filter out unneeded fields that are not even displayed when we fetch the fields here

Copy link
Contributor Author

Choose a reason for hiding this comment

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

ok, changed that

Comment on lines 32 to 39
subject: "Service request: " + serviceCatalogItem.name,
comment: {
body:
"Hi, I would like to request " +
serviceCatalogItem.name +
": " +
serviceCatalogItem.description.substring(0, 100),
},
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: I think using template literals instead of string interpolation makes the code easier to read, like:

subject: `Service Request: ${serviceCatalogItem.name}`

Copy link
Contributor Author

Choose a reason for hiding this comment

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

changed

@@ -5,6 +5,7 @@ export interface TicketField {
title: string;
value?: string | string[] | boolean;
required: boolean;
error: string | null;
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think we need this, there is no error field in the fields returned by the API

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Right, I confused it with Field type

};

const fetchTicketFields = async (
form_id: string,
baseLocale: string
baseLocale: string,
setAssociatedLookupField: (field: Field | null) => void
Copy link
Contributor

Choose a reason for hiding this comment

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

I think that passing a setter callback to a function called fetchTicketFields and then again to a function called isAssociatedLookupField is a bit convoluted and not really clean, since these functions are more getter. Probably it would be better to return both form fields and the lookup field from this function and call the setter outside

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Right, I changed that

e.preventDefault();

const response = await submitServiceItemRequest();
if (response && !response.ok) {
Copy link
Contributor

Choose a reason for hiding this comment

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

This wasn't specified in the Jira story, but we can get different errors from the API, like a 422 when there are user errors or other errors like a 500 for other reasons, and we should handle them differently.

We should check the status code, if it is a 422 we should leverage the already implemented logic, otherwise, we should probably just show an error notification

Copy link
Contributor Author

Choose a reason for hiding this comment

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

ok changed


const response = await submitServiceItemRequest();
if (response && !response.ok) {
const errorData = await response.json();
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: it is probably better to create a type for the entire response and cast it here instead of just creating a type for RequestError and using it later

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ok I added type for response

if (response && !response.ok) {
const errorData = await response.json();
if (errorData.error === "RecordInvalid") {
const invalidFieldErros = errorData.details.base;
Copy link
Contributor

Choose a reason for hiding this comment

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

There is a typo here (Erros)

} else if (response && response.ok) {
const data = await response?.json();
const redirectUrl = "/hc/requests/" + data.request.id;
window.location.href = redirectUrl;
Copy link
Contributor

Choose a reason for hiding this comment

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

We also need to show a success notification on the request page, as shown in the designs. We can use the addFlashNotification function as we do in the answer bot modal:

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ok added. I'm not sure if we will need it also for error but I will need to ask for copy.

Copy link
Contributor

Choose a reason for hiding this comment

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

Do we have an answer to this? IMO if the submission fails for other reasons we would need to show an error somewhere

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@Fredx87 I added notification after different error occurs, still waiting for proper copy but since it is integration branch we can go with this one.

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've just go the copy so I added the correct one

@kubraokcu kubraokcu added the g11n-approved Commented by Globalization label label Nov 26, 2024
export async function submitServiceItemRequest(
serviceCatalogItem: ServiceCatalogItem,
requestFields: Field[],
associatedLookupField: Field | null | undefined,
Copy link
Contributor

Choose a reason for hiding this comment

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

What happens it the field is null? we should prevent this scenario otherwise the submission will not work

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 added check for associatedLookupField

Copy link
Contributor

Choose a reason for hiding this comment

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

I would not add the check here, otherwise what happens is that if we don't find the lookup field for any reason we still show the form, the user submits it and nothing happens. We should stop early, for now we can eventually throw an error if we don't find the lookup field, if we are going to add an Error boundary later. But this function should not accept a null or undefined field

Comment on lines 10 to 54
if (!serviceCatalogItem) {
return;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

We don't need this if since the item cannot be null

Comment on lines 14 to 15
const currentUser =
currentUserRequest.ok && (await currentUserRequest.json());
Copy link
Contributor

Choose a reason for hiding this comment

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

we should throw an error if we cannot get the csrf token

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ok added

e.preventDefault();

const response =
serviceCatalogItem &&
Copy link
Contributor

Choose a reason for hiding this comment

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

Isn't there a case where we will call this function when serviceCatalogItem is null, right? We are showing the button only if it is not null.

If this is the case I would just add a if (!serviceCatalogItem) { return; } at the beginning of the function because using this pattern when the response can be null makes the code difficult to read with a lot of ifs.

I also think we need to handle these edge cases better, if we cannot fetch the custom object we are just not showing the form, and if we don't find the lookup field we don't do anything. These scenarios are blocking because we cannot do anything without this data. We can eventually address this in a separate PR where we introduce error handling

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ok changed to your suggestion. I think I already created some task for adding ErrorBoundary if not I will create and I will add it everywhere it is needed.

} else if (response && response.ok) {
const data = await response?.json();
const redirectUrl = "/hc/requests/" + data.request.id;
window.location.href = redirectUrl;
Copy link
Contributor

Choose a reason for hiding this comment

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

Do we have an answer to this? IMO if the submission fails for other reasons we would need to show an error somewhere

@gosiexon-zen gosiexon-zen force-pushed the mbien/post-request-for-service branch 2 times, most recently from 0fc59b1 to d9b4ea1 Compare November 28, 2024 15:18
Comment on lines 70 to 100
addFlashNotification({
type: "error",
title: t(
"service-catalog.item.service-request-error-title",
"Service couldn't be submitted"
),
message: t(
"service-catalog.item.service-request-error-message",
"Give it a moment and try it again"
),
});
}
Copy link
Contributor

Choose a reason for hiding this comment

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

We need to use useToast from Garden here, addFlashNotification is for adding notifications that are going to be shown on the next page when the user is redirected, but in this case the user is not redirected anywhere. Probably it is confusing since there is no comment on the addFlashNotification function, feel free to add it if you feel like

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ok I changed it to useToast and added the comment to the flash notifications.

export async function submitServiceItemRequest(
serviceCatalogItem: ServiceCatalogItem,
requestFields: Field[],
associatedLookupField: Field | null | undefined,
Copy link
Contributor

Choose a reason for hiding this comment

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

I would not add the check here, otherwise what happens is that if we don't find the lookup field for any reason we still show the form, the user submits it and nothing happens. We should stop early, for now we can eventually throw an error if we don't find the lookup field, if we are going to add an Error boundary later. But this function should not accept a null or undefined field

@gosiexon-zen gosiexon-zen force-pushed the mbien/post-request-for-service branch 2 times, most recently from c9ac420 to 02431f9 Compare December 2, 2024 12:48
const { t } = useTranslation();
const { addToast } = useToast();
const notifyError = useCallback(() => {
Copy link
Contributor

Choose a reason for hiding this comment

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

There shouldn't be the need to wrap this function in a useCallback hook since it is used only in the handleRequestSubmit 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.

ok I removed the useCallback

const { requestFields, associatedLookupField } =
await fetchTicketFields(serviceCatalogItem.form_id, baseLocale);
setRequestFields(requestFields);
setAssociatedLookupField(associatedLookupField);
Copy link
Contributor

Choose a reason for hiding this comment

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

We should save the error state somewhere if don't find the lookup field. We can tackle this separately when we are going to handle the error state, the important thing is that we don't forget to do it

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 will tackle it while handling errors

@gosiexon-zen gosiexon-zen merged commit f386510 into service-catalog-eap Dec 3, 2024
5 checks passed
@gosiexon-zen gosiexon-zen deleted the mbien/post-request-for-service branch December 3, 2024 13:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
g11n-approved Commented by Globalization label
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants