-
Notifications
You must be signed in to change notification settings - Fork 120
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
59ebcd1
commit 90ca504
Showing
22 changed files
with
352 additions
and
128 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,11 +1,19 @@ | ||
import axios from 'axios'; | ||
import { addRequestInterceptor } from './requestInterceptor'; | ||
import { addResponseInterceptor } from './responseInterceptor'; | ||
|
||
const baseApi = axios.create({ | ||
baseURL: `${window.AutoUpgradeVariables.admin_url}/autoupgrade/ajax-upgradetab.php`, | ||
headers: { | ||
'X-Requested-With': 'XMLHttpRequest', | ||
Authorization: `Bearer ${() => window.AutoUpgradeVariables.token}` | ||
} | ||
}, | ||
transitional: { | ||
clarifyTimeoutError: true, | ||
}, | ||
}); | ||
|
||
addRequestInterceptor(baseApi); | ||
addResponseInterceptor(baseApi); | ||
|
||
export default baseApi; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
import { AxiosInstance, InternalAxiosRequestConfig } from "axios"; | ||
|
||
const requestFulfilledInterceptor = (config: InternalAxiosRequestConfig<FormData>) => { | ||
if (!config.data) { | ||
config.data = new FormData(); | ||
} | ||
config.data?.append('dir', window.AutoUpgradeVariables.admin_dir); | ||
return config; | ||
}; | ||
|
||
export const addRequestInterceptor = (axios: AxiosInstance): void => { | ||
axios.interceptors.request.use(requestFulfilledInterceptor); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
import { AxiosError, AxiosInstance, AxiosResponse } from "axios"; | ||
import { APP_ERR_RESPONSE_BAD_TYPE, APP_ERR_RESPONSE_INVALID } from "../types/apiTypes"; | ||
|
||
const responseFulfilledInterceptor = (response: AxiosResponse<any, FormData>) => { | ||
console.log('Checking response', response); | ||
|
||
// All responses must be a parsed JSON. If we get another type of response, | ||
// this means something went wrong, i.e Another software answered. | ||
if (Object.prototype.toString.call(response.data) !== '[object Object]') { | ||
throw new AxiosError('The response does not have a valid type', APP_ERR_RESPONSE_BAD_TYPE, response.config, response.request, response); | ||
} | ||
|
||
// Make sure the response contains the expected data | ||
if (!response.data.kind) { | ||
throw new AxiosError('The response contents is invalid', APP_ERR_RESPONSE_INVALID, response.config, response.request, response); | ||
} | ||
|
||
return response; | ||
}; | ||
|
||
const responseErroredInterceptor = (error: any) => { | ||
const errorSilenced = [AxiosError.ERR_CANCELED]; | ||
// Ignore some errors | ||
if (error instanceof AxiosError) { | ||
if (error.code && errorSilenced.includes(error.code)) { | ||
return Promise.reject(null); | ||
} | ||
} | ||
|
||
return Promise.reject(error); | ||
}; | ||
|
||
export const addResponseInterceptor = (axios: AxiosInstance): void => { | ||
axios.interceptors.response.use(responseFulfilledInterceptor, responseErroredInterceptor); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,23 +1,129 @@ | ||
import DomLifecycle from '../types/DomLifecycle'; | ||
import ErrorPage404 from './error/ErrorPage404'; | ||
import api from '../api/RequestHandler'; | ||
import { ApiError } from '../types/apiTypes'; | ||
import Hydration from '../utils/Hydration'; | ||
import PageAbstract from './PageAbstract'; | ||
|
||
export default class ErrorPage extends PageAbstract { | ||
errorPage?: DomLifecycle; | ||
public static templateId: string = 'error-page-template'; | ||
// TODO: Improve this by putting the target in the template and sent it from the back end | ||
public static targetElementIdToUpdate: string = 'ua_page'; | ||
|
||
isOnHomePage: boolean = false; | ||
|
||
constructor() { | ||
super(); | ||
|
||
if (document.getElementById('ua_error_404')) { | ||
this.errorPage = new ErrorPage404(); | ||
} | ||
this.isOnHomePage = new URLSearchParams(window.location.search).get('route') === 'home-page'; | ||
} | ||
|
||
public mount = (): void => { | ||
this.errorPage?.mount(); | ||
|
||
// If the error page is already present on the DOM (For instance on a whole page refresh), | ||
// initalize it at once instead of waiting for an event. | ||
const errorPageFromBackEnd = document.querySelector('.error-page'); | ||
if (errorPageFromBackEnd) { | ||
this.#mountErrorPage(errorPageFromBackEnd); | ||
} else { | ||
this.#errorTemplateElement.addEventListener(Hydration.hydrationEventName, this.#onError.bind(this), {once: true}); | ||
} | ||
}; | ||
|
||
public beforeDestroy = (): void => { | ||
this.errorPage?.beforeDestroy(); | ||
this.#errorTemplateElement.removeEventListener(Hydration.hydrationEventName, this.#onError.bind(this)); | ||
}; | ||
|
||
get #errorTemplateElement(): HTMLTemplateElement { | ||
const element = document.getElementById(ErrorPage.templateId); | ||
|
||
if (!element) { | ||
throw new Error('Error template not found'); | ||
} | ||
|
||
return element as HTMLTemplateElement; | ||
} | ||
|
||
#onError = async (event: CustomEvent<ApiError>): Promise<void> => { | ||
this.#createErrorPage(event); | ||
} | ||
|
||
#createErrorPage(event: CustomEvent<ApiError>): void { | ||
// Duplicate the error template before alteration | ||
const errorElement = this.#errorTemplateElement.content.cloneNode(true) as DocumentFragment; | ||
|
||
// Set the id of the cloned element | ||
const errorChild = errorElement.getElementById('ua_error_placeholder'); | ||
if (errorChild) { | ||
errorChild.id = `ua_error_${event.detail.type}`; | ||
} | ||
|
||
// If code is a HTTP error number (i.e 404, 500 etc.), let's change the text in the left column with it. | ||
if (typeof event.detail.code === 'number' && event.detail.code >= 300 && event.detail.code.toString().length === 3) { | ||
const strigifiedCode = event.detail.code.toString().replaceAll('0', 'O'); | ||
const errorCodeSlotElements = errorElement.querySelectorAll('.error-page__code-char'); | ||
errorCodeSlotElements.forEach((element: Element, index: number) => { | ||
element.innerHTML = strigifiedCode[index]; | ||
}); | ||
errorElement.querySelector('.error-page__code-missing')?.classList.add('hidden'); | ||
} | ||
|
||
// Display a user friendly text related to the code if it exists, otherwise write the error code. | ||
const errorDescriptionElement = errorElement.querySelector('.error-page__desc'); | ||
const userFriendlyDescriptionElement = errorDescriptionElement?.querySelector(`.error-page__desc-${event.detail.code || event.detail.type}`); | ||
if (userFriendlyDescriptionElement) { | ||
userFriendlyDescriptionElement.classList.remove('hidden'); | ||
} else if (errorDescriptionElement && event.detail.type) { | ||
errorDescriptionElement.innerHTML = event.detail.type; | ||
} | ||
|
||
// Store the contents in the hidden field so it can be used in the error reporting modal | ||
const additionalContentsElement = errorElement.querySelector('.error-page__contents'); | ||
if (additionalContentsElement && event.detail.additionalContents) { | ||
additionalContentsElement.innerHTML = new String(event.detail.additionalContents).toString(); | ||
} | ||
|
||
// Finally, append the result on the page | ||
const targetElementToUpdate = document.getElementById(ErrorPage.targetElementIdToUpdate); | ||
if (!targetElementToUpdate) { | ||
throw new Error('Target element cannot be found'); | ||
} | ||
targetElementToUpdate.replaceChildren(errorElement); | ||
|
||
// Enable events and page features | ||
this.#mountErrorPage(document.querySelector('.error-page')!); | ||
} | ||
|
||
#mountErrorPage(errorPage: Element): void { | ||
console.log('mounting', errorPage); | ||
this.#form.addEventListener('submit', this.#onSubmit, {once: true}); | ||
|
||
// Display the proper action buttons | ||
const activeButtonElement = this.isOnHomePage | ||
? errorPage.querySelector('.error-page__exit-button') | ||
: errorPage.querySelector('.error-page__home-page-form'); | ||
|
||
if (activeButtonElement) { | ||
activeButtonElement.classList.remove('hidden'); | ||
} | ||
} | ||
|
||
get #form(): HTMLFormElement { | ||
const form = document.forms.namedItem('home-page-form'); | ||
if (!form) { | ||
throw new Error('Form not found'); | ||
} | ||
|
||
['routeToSubmit'].forEach((data) => { | ||
if (!form.dataset[data]) { | ||
throw new Error(`Missing data ${data} from form dataset.`); | ||
} | ||
}); | ||
|
||
return form; | ||
} | ||
|
||
readonly #onSubmit = async (event: Event): Promise<void> => { | ||
event.preventDefault(); | ||
|
||
await api.post(this.#form.dataset.routeToSubmit!, new FormData(this.#form)); | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.