2020-02-09 10:21:13 +00:00
|
|
|
import React from 'react';
|
|
|
|
import { withSnackbar, WithSnackbarProps } from 'notistack';
|
|
|
|
|
|
|
|
import { redirectingAuthorizedFetch } from '../authentication';
|
|
|
|
|
|
|
|
export interface RestControllerProps<D> extends WithSnackbarProps {
|
|
|
|
handleValueChange: (name: keyof D) => (event: React.ChangeEvent<HTMLInputElement>) => void;
|
|
|
|
|
2020-05-14 22:23:45 +00:00
|
|
|
setData: (data: D, callback?: () => void) => void;
|
2020-02-09 10:21:13 +00:00
|
|
|
saveData: () => void;
|
|
|
|
loadData: () => void;
|
|
|
|
|
|
|
|
data?: D;
|
|
|
|
loading: boolean;
|
|
|
|
errorMessage?: string;
|
|
|
|
}
|
|
|
|
|
2020-05-14 22:23:45 +00:00
|
|
|
export const extractEventValue = (event: React.ChangeEvent<HTMLInputElement>) => {
|
2020-02-27 00:05:38 +00:00
|
|
|
switch (event.target.type) {
|
|
|
|
case "number":
|
|
|
|
return event.target.valueAsNumber;
|
|
|
|
case "checkbox":
|
|
|
|
return event.target.checked;
|
|
|
|
default:
|
|
|
|
return event.target.value
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-14 22:23:45 +00:00
|
|
|
interface RestControllerState<D> {
|
|
|
|
data?: D;
|
|
|
|
loading: boolean;
|
|
|
|
errorMessage?: string;
|
|
|
|
}
|
|
|
|
|
2020-02-09 10:21:13 +00:00
|
|
|
export function restController<D, P extends RestControllerProps<D>>(endpointUrl: string, RestController: React.ComponentType<P & RestControllerProps<D>>) {
|
|
|
|
return withSnackbar(
|
|
|
|
class extends React.Component<Omit<P, keyof RestControllerProps<D>> & WithSnackbarProps, RestControllerState<D>> {
|
|
|
|
|
|
|
|
state: RestControllerState<D> = {
|
|
|
|
data: undefined,
|
|
|
|
loading: false,
|
|
|
|
errorMessage: undefined
|
|
|
|
};
|
|
|
|
|
2020-05-14 22:23:45 +00:00
|
|
|
setData = (data: D, callback?: () => void) => {
|
2020-02-09 10:21:13 +00:00
|
|
|
this.setState({
|
|
|
|
data,
|
|
|
|
loading: false,
|
|
|
|
errorMessage: undefined
|
2020-05-14 22:23:45 +00:00
|
|
|
}, callback);
|
2020-02-09 10:21:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
loadData = () => {
|
|
|
|
this.setState({
|
|
|
|
data: undefined,
|
|
|
|
loading: true,
|
|
|
|
errorMessage: undefined
|
|
|
|
});
|
|
|
|
redirectingAuthorizedFetch(endpointUrl).then(response => {
|
|
|
|
if (response.status === 200) {
|
|
|
|
return response.json();
|
|
|
|
}
|
|
|
|
throw Error("Invalid status code: " + response.status);
|
|
|
|
}).then(json => {
|
|
|
|
this.setState({ data: json, loading: false })
|
|
|
|
}).catch(error => {
|
|
|
|
const errorMessage = error.message || "Unknown error";
|
|
|
|
this.props.enqueueSnackbar("Problem fetching: " + errorMessage, { variant: 'error' });
|
|
|
|
this.setState({ data: undefined, loading: false, errorMessage });
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
saveData = () => {
|
|
|
|
this.setState({ loading: true });
|
|
|
|
redirectingAuthorizedFetch(endpointUrl, {
|
|
|
|
method: 'POST',
|
|
|
|
body: JSON.stringify(this.state.data),
|
|
|
|
headers: {
|
|
|
|
'Content-Type': 'application/json'
|
|
|
|
}
|
|
|
|
}).then(response => {
|
|
|
|
if (response.status === 200) {
|
|
|
|
return response.json();
|
|
|
|
}
|
|
|
|
throw Error("Invalid status code: " + response.status);
|
|
|
|
}).then(json => {
|
|
|
|
this.props.enqueueSnackbar("Changes successfully applied.", { variant: 'success' });
|
|
|
|
this.setState({ data: json, loading: false });
|
|
|
|
}).catch(error => {
|
|
|
|
const errorMessage = error.message || "Unknown error";
|
|
|
|
this.props.enqueueSnackbar("Problem saving: " + errorMessage, { variant: 'error' });
|
|
|
|
this.setState({ data: undefined, loading: false, errorMessage });
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
handleValueChange = (name: keyof D) => (event: React.ChangeEvent<HTMLInputElement>) => {
|
2020-05-14 22:23:45 +00:00
|
|
|
const data = { ...this.state.data!, [name]: extractEventValue(event) };
|
2020-02-09 10:21:13 +00:00
|
|
|
this.setState({ data });
|
|
|
|
}
|
|
|
|
|
|
|
|
render() {
|
|
|
|
return <RestController
|
|
|
|
handleValueChange={this.handleValueChange}
|
|
|
|
setData={this.setData}
|
|
|
|
saveData={this.saveData}
|
|
|
|
loadData={this.loadData}
|
|
|
|
{...this.state}
|
|
|
|
{...this.props as P}
|
|
|
|
/>;
|
|
|
|
}
|
|
|
|
|
|
|
|
});
|
|
|
|
}
|