delete resource

master
Tudor Stanciu 2022-12-18 03:19:48 +02:00
parent 79f9ce0aca
commit 453dfa96ae
7 changed files with 256 additions and 68 deletions

View File

@ -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
};
};

View File

@ -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;

View File

@ -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>
</>

View File

@ -1,18 +1,37 @@
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}>
@ -59,6 +78,47 @@ const ResourceComponent = ({ resource, mimeTypes, onPropertyChange }) => {
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>
<Grid item xs={12}>
@ -71,15 +131,43 @@ const ResourceComponent = ({ resource, mimeTypes, onPropertyChange }) => {
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>
</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;

View File

@ -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>
</>

View File

@ -12,6 +12,9 @@ const style = (theme) => {
display: "block",
maxWidth: "100%",
maxHeight: "100%"
},
button: {
marginRight: theme.spacing(1)
}
};
};

View File

@ -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]