delete resource
parent
79f9ce0aca
commit
453dfa96ae
|
@ -1,6 +1,6 @@
|
||||||
import { useCallback } from "react";
|
import { useCallback } from "react";
|
||||||
import useHttpRequest from "../../../hooks/useHttpRequest";
|
import useHttpRequest from "../../../hooks/useHttpRequest";
|
||||||
import { get } from "../../../utils/axios";
|
import { get, del } from "../../../utils/axios";
|
||||||
import { defaultResourcesFilters } from "../../../constants/resourcesConstants";
|
import { defaultResourcesFilters } from "../../../constants/resourcesConstants";
|
||||||
|
|
||||||
const cdn = process.env.REACT_APP_CDN_URL;
|
const cdn = process.env.REACT_APP_CDN_URL;
|
||||||
|
@ -50,9 +50,19 @@ const useResourcesApi = () => {
|
||||||
[exec]
|
[exec]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const deleteResource = useCallback(
|
||||||
|
(resourceId, options) => {
|
||||||
|
const endpoint = `${cdn}/admin/resource?ResourceId=${resourceId}`;
|
||||||
|
const promise = exec(() => del(endpoint), options);
|
||||||
|
return promise;
|
||||||
|
},
|
||||||
|
[exec]
|
||||||
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
getResources,
|
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}>
|
<Grid item xs={12} sm={9}>
|
||||||
{isAutomaticMimeType ? (
|
{isAutomaticMimeType ? (
|
||||||
<TextField
|
<TextField
|
||||||
id="resource-path"
|
id="automatic-resource-mime-type"
|
||||||
label="Path on disk"
|
label="Automatic MIME type"
|
||||||
fullWidth
|
fullWidth
|
||||||
disabled
|
disabled
|
||||||
value={mimeType.mimeTypeName}
|
value={mimeType.mimeTypeName}
|
||||||
|
@ -58,7 +58,7 @@ const MimeTypeComponent = ({ mimeType, mimeTypes, onPropertyChange }) => {
|
||||||
color="primary"
|
color="primary"
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
label="Auto mime type"
|
label="Auto MIME type"
|
||||||
/>
|
/>
|
||||||
</Grid>
|
</Grid>
|
||||||
</>
|
</>
|
||||||
|
|
|
@ -1,18 +1,37 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import PropTypes from "prop-types";
|
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 { makeStyles } from "@material-ui/core/styles";
|
||||||
import style from "../styles";
|
import style from "../styles";
|
||||||
import ResourcePreviewComponent from "./ResourcePreviewComponent";
|
import ResourcePreviewComponent from "./ResourcePreviewComponent";
|
||||||
import MimeTypeComponent from "./MimeTypeComponent";
|
import MimeTypeComponent from "./MimeTypeComponent";
|
||||||
|
import DeleteDialog from "./DeleteDialog";
|
||||||
import { onTextFieldChange } from "../../../../utils/adapters";
|
import { onTextFieldChange } from "../../../../utils/adapters";
|
||||||
|
|
||||||
const useStyles = makeStyles(style);
|
const useStyles = makeStyles(style);
|
||||||
|
|
||||||
const ResourceComponent = ({ resource, mimeTypes, onPropertyChange }) => {
|
const ResourceComponent = ({
|
||||||
|
resource,
|
||||||
|
mimeTypes,
|
||||||
|
resourceCategories,
|
||||||
|
onPropertyChange,
|
||||||
|
deleteProps,
|
||||||
|
processing
|
||||||
|
}) => {
|
||||||
const classes = useStyles();
|
const classes = useStyles();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<>
|
||||||
<Paper className={classes.paper}>
|
<Paper className={classes.paper}>
|
||||||
<Grid container spacing={2}>
|
<Grid container spacing={2}>
|
||||||
<Grid item xs={12} sm={5}>
|
<Grid item xs={12} sm={5}>
|
||||||
|
@ -59,6 +78,47 @@ const ResourceComponent = ({ resource, mimeTypes, onPropertyChange }) => {
|
||||||
mimeTypes={mimeTypes}
|
mimeTypes={mimeTypes}
|
||||||
onPropertyChange={onPropertyChange}
|
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>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid item xs={12}>
|
<Grid item xs={12}>
|
||||||
|
@ -71,15 +131,43 @@ const ResourceComponent = ({ resource, mimeTypes, onPropertyChange }) => {
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
/>
|
/>
|
||||||
</Grid>
|
</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>
|
||||||
</Paper>
|
</Paper>
|
||||||
|
<DeleteDialog {...deleteProps} title={resource.resourceName} />
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
ResourceComponent.propTypes = {
|
ResourceComponent.propTypes = {
|
||||||
resource: PropTypes.object.isRequired,
|
resource: PropTypes.object.isRequired,
|
||||||
mimeTypes: PropTypes.array.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;
|
export default ResourceComponent;
|
||||||
|
|
|
@ -6,18 +6,23 @@ import { LoadingText } from "../../../../components";
|
||||||
import ResourceComponent from "./ResourceComponent";
|
import ResourceComponent from "./ResourceComponent";
|
||||||
import { makeStyles } from "@material-ui/core/styles";
|
import { makeStyles } from "@material-ui/core/styles";
|
||||||
import style from "../styles";
|
import style from "../styles";
|
||||||
|
import { useToast } from "../../../../hooks";
|
||||||
|
|
||||||
const useStyles = makeStyles(style);
|
const useStyles = makeStyles(style);
|
||||||
|
|
||||||
const ResourceContainer = () => {
|
const ResourceContainer = () => {
|
||||||
const [state, setState] = useState(null);
|
const [state, setState] = useState(null);
|
||||||
const [loading, setLoading] = useState(false);
|
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 classes = useStyles();
|
||||||
const params = useParams();
|
const params = useParams();
|
||||||
const { getResource } = useResourcesApi();
|
const { getResource, deleteResource } = useResourcesApi();
|
||||||
const { getMimeTypes } = useDictionariesApi();
|
const { getMimeTypes, getResourceCategories } = useDictionariesApi();
|
||||||
|
const { success } = useToast();
|
||||||
|
|
||||||
const isNew = useMemo(() => params.id === "new", [params.id]);
|
const isNew = useMemo(() => params.id === "new", [params.id]);
|
||||||
|
|
||||||
|
@ -25,6 +30,10 @@ const ResourceContainer = () => {
|
||||||
getMimeTypes().then((r) => setMimeTypes(r));
|
getMimeTypes().then((r) => setMimeTypes(r));
|
||||||
}, [getMimeTypes]);
|
}, [getMimeTypes]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
getResourceCategories().then((r) => setResourceCategories(r));
|
||||||
|
}, [getResourceCategories]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isNew) return;
|
if (isNew) return;
|
||||||
const resourceId = parseInt(params.id);
|
const resourceId = parseInt(params.id);
|
||||||
|
@ -39,7 +48,17 @@ const ResourceContainer = () => {
|
||||||
setState((prev) => ({ ...prev, [prop]: value }));
|
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 (
|
return (
|
||||||
<>
|
<>
|
||||||
|
@ -48,7 +67,15 @@ const ResourceContainer = () => {
|
||||||
<ResourceComponent
|
<ResourceComponent
|
||||||
resource={state}
|
resource={state}
|
||||||
mimeTypes={mimeTypes}
|
mimeTypes={mimeTypes}
|
||||||
|
resourceCategories={resourceCategories}
|
||||||
onPropertyChange={handlePropertyChange}
|
onPropertyChange={handlePropertyChange}
|
||||||
|
deleteProps={{
|
||||||
|
open: deleteDialogOpen,
|
||||||
|
onOpen: () => setDeleteDialogOpen(true),
|
||||||
|
onClose: () => setDeleteDialogOpen(false),
|
||||||
|
onConfirm: () => handleDelete(state.resourceId)
|
||||||
|
}}
|
||||||
|
processing={processing}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
|
|
|
@ -12,6 +12,9 @@ const style = (theme) => {
|
||||||
display: "block",
|
display: "block",
|
||||||
maxWidth: "100%",
|
maxWidth: "100%",
|
||||||
maxHeight: "100%"
|
maxHeight: "100%"
|
||||||
|
},
|
||||||
|
button: {
|
||||||
|
marginRight: theme.spacing(1)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -6,7 +6,24 @@ const useHttpRequest = () => {
|
||||||
|
|
||||||
const handleError = useCallback(
|
const handleError = useCallback(
|
||||||
(err) => {
|
(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(message);
|
||||||
},
|
},
|
||||||
[error]
|
[error]
|
||||||
|
|
Loading…
Reference in New Issue