delete resource
parent
79f9ce0aca
commit
453dfa96ae
|
@ -1,6 +1,6 @@
|
|||
import { useCallback } from "react";
|
||||
import useHttpRequest from "../../../hooks/useHttpRequest";
|
||||
import { get } from "../../../utils/axios";
|
||||
import { get, del } from "../../../utils/axios";
|
||||
import { defaultResourcesFilters } from "../../../constants/resourcesConstants";
|
||||
|
||||
const cdn = process.env.REACT_APP_CDN_URL;
|
||||
|
@ -50,9 +50,19 @@ const useResourcesApi = () => {
|
|||
[exec]
|
||||
);
|
||||
|
||||
const deleteResource = useCallback(
|
||||
(resourceId, options) => {
|
||||
const endpoint = `${cdn}/admin/resource?ResourceId=${resourceId}`;
|
||||
const promise = exec(() => del(endpoint), options);
|
||||
return promise;
|
||||
},
|
||||
[exec]
|
||||
);
|
||||
|
||||
return {
|
||||
getResources,
|
||||
getResource
|
||||
getResource,
|
||||
deleteResource
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
import React from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import Button from "@material-ui/core/Button";
|
||||
import Dialog from "@material-ui/core/Dialog";
|
||||
import DialogActions from "@material-ui/core/DialogActions";
|
||||
import DialogContent from "@material-ui/core/DialogContent";
|
||||
import DialogContentText from "@material-ui/core/DialogContentText";
|
||||
import DialogTitle from "@material-ui/core/DialogTitle";
|
||||
|
||||
const DeleteDialog = ({ open, onClose, onConfirm, title }) => {
|
||||
return (
|
||||
<Dialog
|
||||
open={open}
|
||||
onClose={onClose}
|
||||
aria-labelledby="alert-dialog-title"
|
||||
aria-describedby="alert-dialog-description"
|
||||
>
|
||||
<DialogTitle id="alert-dialog-title">{title}</DialogTitle>
|
||||
<DialogContent>
|
||||
<DialogContentText id="alert-dialog-description">
|
||||
{`Are you sure you want to delete the resource '${title}'?`}
|
||||
</DialogContentText>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button onClick={onClose} color="primary">
|
||||
No
|
||||
</Button>
|
||||
<Button onClick={onConfirm} color="secondary" autoFocus>
|
||||
Yes
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
|
||||
DeleteDialog.propTypes = {
|
||||
open: PropTypes.bool.isRequired,
|
||||
onClose: PropTypes.func.isRequired,
|
||||
onConfirm: PropTypes.func.isRequired,
|
||||
title: PropTypes.string.isRequired
|
||||
};
|
||||
|
||||
export default DeleteDialog;
|
|
@ -19,8 +19,8 @@ const MimeTypeComponent = ({ mimeType, mimeTypes, onPropertyChange }) => {
|
|||
<Grid item xs={12} sm={9}>
|
||||
{isAutomaticMimeType ? (
|
||||
<TextField
|
||||
id="resource-path"
|
||||
label="Path on disk"
|
||||
id="automatic-resource-mime-type"
|
||||
label="Automatic MIME type"
|
||||
fullWidth
|
||||
disabled
|
||||
value={mimeType.mimeTypeName}
|
||||
|
@ -58,7 +58,7 @@ const MimeTypeComponent = ({ mimeType, mimeTypes, onPropertyChange }) => {
|
|||
color="primary"
|
||||
/>
|
||||
}
|
||||
label="Auto mime type"
|
||||
label="Auto MIME type"
|
||||
/>
|
||||
</Grid>
|
||||
</>
|
||||
|
|
|
@ -1,85 +1,173 @@
|
|||
import React from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import { Grid, Paper, ButtonBase, TextField } from "@material-ui/core";
|
||||
import {
|
||||
Grid,
|
||||
Paper,
|
||||
ButtonBase,
|
||||
TextField,
|
||||
FormControlLabel,
|
||||
Checkbox,
|
||||
Button
|
||||
} from "@material-ui/core";
|
||||
import Autocomplete from "@material-ui/lab/Autocomplete";
|
||||
import { Save as SaveIcon, Delete as DeleteIcon } from "@material-ui/icons";
|
||||
import { makeStyles } from "@material-ui/core/styles";
|
||||
import style from "../styles";
|
||||
import ResourcePreviewComponent from "./ResourcePreviewComponent";
|
||||
import MimeTypeComponent from "./MimeTypeComponent";
|
||||
import DeleteDialog from "./DeleteDialog";
|
||||
import { onTextFieldChange } from "../../../../utils/adapters";
|
||||
|
||||
const useStyles = makeStyles(style);
|
||||
|
||||
const ResourceComponent = ({ resource, mimeTypes, onPropertyChange }) => {
|
||||
const ResourceComponent = ({
|
||||
resource,
|
||||
mimeTypes,
|
||||
resourceCategories,
|
||||
onPropertyChange,
|
||||
deleteProps,
|
||||
processing
|
||||
}) => {
|
||||
const classes = useStyles();
|
||||
|
||||
return (
|
||||
<Paper className={classes.paper}>
|
||||
<Grid container spacing={2}>
|
||||
<Grid item xs={12} sm={5}>
|
||||
<ButtonBase>
|
||||
<ResourcePreviewComponent resource={resource} />
|
||||
</ButtonBase>
|
||||
</Grid>
|
||||
<Grid item xs={12} sm={7}>
|
||||
<Grid container spacing={2}>
|
||||
<Grid item xs={12} sm={6}>
|
||||
<TextField
|
||||
id="resource-code"
|
||||
label="Code"
|
||||
fullWidth
|
||||
required
|
||||
value={resource.resourceCode}
|
||||
onChange={onTextFieldChange(onPropertyChange("resourceCode"))}
|
||||
<>
|
||||
<Paper className={classes.paper}>
|
||||
<Grid container spacing={2}>
|
||||
<Grid item xs={12} sm={5}>
|
||||
<ButtonBase>
|
||||
<ResourcePreviewComponent resource={resource} />
|
||||
</ButtonBase>
|
||||
</Grid>
|
||||
<Grid item xs={12} sm={7}>
|
||||
<Grid container spacing={2}>
|
||||
<Grid item xs={12} sm={6}>
|
||||
<TextField
|
||||
id="resource-code"
|
||||
label="Code"
|
||||
fullWidth
|
||||
required
|
||||
value={resource.resourceCode}
|
||||
onChange={onTextFieldChange(onPropertyChange("resourceCode"))}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12} sm={6}>
|
||||
<TextField
|
||||
id="resource-name"
|
||||
label="Name"
|
||||
fullWidth
|
||||
required
|
||||
value={resource.resourceName}
|
||||
onChange={onTextFieldChange(onPropertyChange("resourceName"))}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<TextField
|
||||
id="resource-path"
|
||||
label="Path on disk"
|
||||
fullWidth
|
||||
disabled
|
||||
value={resource.resourcePath}
|
||||
/>
|
||||
</Grid>
|
||||
<MimeTypeComponent
|
||||
mimeType={{
|
||||
mimeTypeId: resource.mimeTypeId,
|
||||
mimeTypeName: resource.mimeType
|
||||
}}
|
||||
mimeTypes={mimeTypes}
|
||||
onPropertyChange={onPropertyChange}
|
||||
/>
|
||||
<Grid item xs={12} sm={9}>
|
||||
<Autocomplete
|
||||
id="resource-category"
|
||||
options={resourceCategories}
|
||||
value={resource.categoryId}
|
||||
onChange={(_event, value, _reason, _details) =>
|
||||
onPropertyChange("categoryId")(value.categoryId)
|
||||
}
|
||||
getOptionLabel={(option) => {
|
||||
const optionIsObject =
|
||||
typeof option === "object" && option !== null;
|
||||
if (optionIsObject) return option.categoryName;
|
||||
|
||||
const category = resourceCategories.find(
|
||||
(z) => z.categoryId === option
|
||||
);
|
||||
return category.categoryName;
|
||||
}}
|
||||
getOptionSelected={(option, value) =>
|
||||
option.categoryId === value
|
||||
}
|
||||
renderInput={(params) => (
|
||||
<TextField {...params} label="Category" />
|
||||
)}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12} sm={3}>
|
||||
<FormControlLabel
|
||||
control={
|
||||
<Checkbox
|
||||
checked={resource.secured}
|
||||
onChange={(event) =>
|
||||
onPropertyChange("secured")(event.target.checked)
|
||||
}
|
||||
name="resources-is-secured"
|
||||
color="primary"
|
||||
/>
|
||||
}
|
||||
label="Secured"
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Grid item xs={12} sm={6}>
|
||||
<TextField
|
||||
id="resource-name"
|
||||
label="Name"
|
||||
fullWidth
|
||||
required
|
||||
value={resource.resourceName}
|
||||
onChange={onTextFieldChange(onPropertyChange("resourceName"))}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<TextField
|
||||
id="resource-path"
|
||||
label="Path on disk"
|
||||
fullWidth
|
||||
disabled
|
||||
value={resource.resourcePath}
|
||||
/>
|
||||
</Grid>
|
||||
<MimeTypeComponent
|
||||
mimeType={{
|
||||
mimeTypeId: resource.mimeTypeId,
|
||||
mimeTypeName: resource.mimeType
|
||||
}}
|
||||
mimeTypes={mimeTypes}
|
||||
onPropertyChange={onPropertyChange}
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<TextField
|
||||
fullWidth
|
||||
multiline
|
||||
rows={12}
|
||||
id="resource-description"
|
||||
label="Description"
|
||||
variant="outlined"
|
||||
/>
|
||||
</Grid>
|
||||
<Grid container justify="flex-end">
|
||||
<Grid item>
|
||||
<Button
|
||||
variant="outlined"
|
||||
color="secondary"
|
||||
className={classes.button}
|
||||
startIcon={<DeleteIcon />}
|
||||
onClick={deleteProps.onOpen}
|
||||
disabled={!!processing}
|
||||
>
|
||||
{processing === "delete" ? "Deleting" : "Delete"}
|
||||
</Button>
|
||||
<Button
|
||||
variant="outlined"
|
||||
color="primary"
|
||||
className={classes.button}
|
||||
startIcon={<SaveIcon />}
|
||||
disabled={!!processing}
|
||||
>
|
||||
{processing === "delete" ? "Saving" : "Save"}
|
||||
</Button>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<TextField
|
||||
fullWidth
|
||||
multiline
|
||||
rows={12}
|
||||
id="resource-description"
|
||||
label="Description"
|
||||
variant="outlined"
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Paper>
|
||||
</Paper>
|
||||
<DeleteDialog {...deleteProps} title={resource.resourceName} />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
ResourceComponent.propTypes = {
|
||||
resource: PropTypes.object.isRequired,
|
||||
mimeTypes: PropTypes.array.isRequired,
|
||||
onPropertyChange: PropTypes.func.isRequired
|
||||
resourceCategories: PropTypes.array.isRequired,
|
||||
onPropertyChange: PropTypes.func.isRequired,
|
||||
deleteProps: PropTypes.object.isRequired,
|
||||
processing: PropTypes.bool.isRequired
|
||||
};
|
||||
|
||||
export default ResourceComponent;
|
||||
|
|
|
@ -6,18 +6,23 @@ import { LoadingText } from "../../../../components";
|
|||
import ResourceComponent from "./ResourceComponent";
|
||||
import { makeStyles } from "@material-ui/core/styles";
|
||||
import style from "../styles";
|
||||
import { useToast } from "../../../../hooks";
|
||||
|
||||
const useStyles = makeStyles(style);
|
||||
|
||||
const ResourceContainer = () => {
|
||||
const [state, setState] = useState(null);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [mimeTypes, setMimeTypes] = useState([]);
|
||||
const [mimeTypes, setMimeTypes] = useState(null);
|
||||
const [resourceCategories, setResourceCategories] = useState(null);
|
||||
const [deleteDialogOpen, setDeleteDialogOpen] = useState(false);
|
||||
const [processing, setProcessing] = useState(null);
|
||||
|
||||
const classes = useStyles();
|
||||
const params = useParams();
|
||||
const { getResource } = useResourcesApi();
|
||||
const { getMimeTypes } = useDictionariesApi();
|
||||
const { getResource, deleteResource } = useResourcesApi();
|
||||
const { getMimeTypes, getResourceCategories } = useDictionariesApi();
|
||||
const { success } = useToast();
|
||||
|
||||
const isNew = useMemo(() => params.id === "new", [params.id]);
|
||||
|
||||
|
@ -25,6 +30,10 @@ const ResourceContainer = () => {
|
|||
getMimeTypes().then((r) => setMimeTypes(r));
|
||||
}, [getMimeTypes]);
|
||||
|
||||
useEffect(() => {
|
||||
getResourceCategories().then((r) => setResourceCategories(r));
|
||||
}, [getResourceCategories]);
|
||||
|
||||
useEffect(() => {
|
||||
if (isNew) return;
|
||||
const resourceId = parseInt(params.id);
|
||||
|
@ -39,7 +48,17 @@ const ResourceContainer = () => {
|
|||
setState((prev) => ({ ...prev, [prop]: value }));
|
||||
};
|
||||
|
||||
if (loading || state === null) return <LoadingText lines={15} onPaper />;
|
||||
const handleDelete = async (resourceId) => {
|
||||
setProcessing("delete");
|
||||
const response = await deleteResource(resourceId);
|
||||
if (response && response.resourceId === state.resourceId)
|
||||
success(`Resource '${state.resourceName}' was successfully deleted.`);
|
||||
setProcessing(null);
|
||||
setDeleteDialogOpen(false);
|
||||
};
|
||||
|
||||
if (loading || !state || !mimeTypes || !resourceCategories)
|
||||
return <LoadingText lines={15} onPaper />;
|
||||
|
||||
return (
|
||||
<>
|
||||
|
@ -48,7 +67,15 @@ const ResourceContainer = () => {
|
|||
<ResourceComponent
|
||||
resource={state}
|
||||
mimeTypes={mimeTypes}
|
||||
resourceCategories={resourceCategories}
|
||||
onPropertyChange={handlePropertyChange}
|
||||
deleteProps={{
|
||||
open: deleteDialogOpen,
|
||||
onOpen: () => setDeleteDialogOpen(true),
|
||||
onClose: () => setDeleteDialogOpen(false),
|
||||
onConfirm: () => handleDelete(state.resourceId)
|
||||
}}
|
||||
processing={processing}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
|
|
|
@ -12,6 +12,9 @@ const style = (theme) => {
|
|||
display: "block",
|
||||
maxWidth: "100%",
|
||||
maxHeight: "100%"
|
||||
},
|
||||
button: {
|
||||
marginRight: theme.spacing(1)
|
||||
}
|
||||
};
|
||||
};
|
||||
|
|
|
@ -6,7 +6,24 @@ const useHttpRequest = () => {
|
|||
|
||||
const handleError = useCallback(
|
||||
(err) => {
|
||||
const message = `${err.title} ${err.correlationId}`;
|
||||
let message;
|
||||
switch (err?.statusCode) {
|
||||
case 500:
|
||||
message = "Internal server error.";
|
||||
break;
|
||||
|
||||
case 404:
|
||||
message = err.serverMessage;
|
||||
break;
|
||||
|
||||
case 401:
|
||||
message = `Unauthorized: ${err.serverMessage}`;
|
||||
break;
|
||||
|
||||
default:
|
||||
message = "An unexpected error has occurred.";
|
||||
}
|
||||
|
||||
error(message);
|
||||
},
|
||||
[error]
|
||||
|
|
Loading…
Reference in New Issue