-
Notifications
You must be signed in to change notification settings - Fork 410
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
Post request for service catalog item #547
Conversation
@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 |
import type { Field } from "../ticket-fields"; | ||
import type { ServiceCatalogItem } from "./data-types/ServiceCatalogItem"; | ||
|
||
export function useServiceFormSubmit( |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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(); |
There was a problem hiding this comment.
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
const customFields = requestFields.map((field) => { | ||
if (field.type !== "subject" && field.type !== "description") { | ||
return { | ||
id: field.id, | ||
value: field.value, | ||
}; | ||
} else return; | ||
}); |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok, changed that
subject: "Service request: " + serviceCatalogItem.name, | ||
comment: { | ||
body: | ||
"Hi, I would like to request " + | ||
serviceCatalogItem.name + | ||
": " + | ||
serviceCatalogItem.description.substring(0, 100), | ||
}, |
There was a problem hiding this comment.
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}`
There was a problem hiding this comment.
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; |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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) { |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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(); |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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; |
There was a problem hiding this comment.
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; |
There was a problem hiding this comment.
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:
copenhagen_theme/src/modules/new-request-form/answer-bot-modal/AnswerBotModal.tsx
Line 107 in a9a12f0
addFlashNotification({ |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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
22a589f
to
79fd420
Compare
export async function submitServiceItemRequest( | ||
serviceCatalogItem: ServiceCatalogItem, | ||
requestFields: Field[], | ||
associatedLookupField: Field | null | undefined, |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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
if (!serviceCatalogItem) { | ||
return; | ||
} |
There was a problem hiding this comment.
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
const currentUser = | ||
currentUserRequest.ok && (await currentUserRequest.json()); |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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 && |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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; |
There was a problem hiding this comment.
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
0fc59b1
to
d9b4ea1
Compare
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" | ||
), | ||
}); | ||
} |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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, |
There was a problem hiding this comment.
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
c9ac420
to
02431f9
Compare
const { t } = useTranslation(); | ||
const { addToast } = useToast(); | ||
const notifyError = useCallback(() => { |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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); |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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
02431f9
to
81071cc
Compare
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:
Requested service view:
Checklist