resource links component

master
Tudor Stanciu 2022-12-18 22:50:32 +02:00
parent 25a2ff95e4
commit 1e45226205
8 changed files with 148 additions and 18 deletions

View File

@ -42,7 +42,10 @@
"Edit": "Edit", "Edit": "Edit",
"More": "More", "More": "More",
"OpenInNewTab": "Open in new tab", "OpenInNewTab": "Open in new tab",
"Delete": "Delete" "Delete": "Delete",
"Deleting": "Deleting",
"Save": "Save",
"Saving": "Saving"
}, },
"Menu": { "Menu": {
"Dashboard": "Dashboard", "Dashboard": "Dashboard",
@ -75,6 +78,10 @@
"Name": "Name", "Name": "Name",
"Category": "Category", "Category": "Category",
"Secured": "Secured", "Secured": "Secured",
"PathOnDisk": "Path on disk",
"MimeType": "MIME type",
"AutomaticMimeType": "Automatic MIME type",
"Description": "Description",
"List": { "List": {
"Title": "Resource management", "Title": "Resource management",
"SubTitle": "Resources", "SubTitle": "Resources",
@ -87,6 +94,10 @@
"CopyUrl": "Copy resource URL", "CopyUrl": "Copy resource URL",
"LinkCopiedToClipboard": "The link has been copied to the clipboard." "LinkCopiedToClipboard": "The link has been copied to the clipboard."
} }
},
"Links": {
"Title": "Links",
"SubTitle": "Different forms of the URL at which a resource can be accessed"
} }
}, },
"ServerAvailability": { "ServerAvailability": {

View File

@ -33,7 +33,10 @@
"Edit": "Editează", "Edit": "Editează",
"More": "Mai mult", "More": "Mai mult",
"OpenInNewTab": "Deschide într-un tab nou", "OpenInNewTab": "Deschide într-un tab nou",
"Delete": "Șterge" "Delete": "Șterge",
"Deleting": "Se şterge",
"Save": "Salvează",
"Saving": "Se salvează"
}, },
"Menu": { "Menu": {
"Dashboard": "Dashboard", "Dashboard": "Dashboard",
@ -66,6 +69,10 @@
"Name": "Nume", "Name": "Nume",
"Category": "Categorie", "Category": "Categorie",
"Secured": "Securizat", "Secured": "Securizat",
"PathOnDisk": "Cale pe disc",
"MimeType": "Tip MIME",
"AutomaticMimeType": "Tip MIME automat",
"Description": "Descriere",
"List": { "List": {
"Title": "Managementul resurselor", "Title": "Managementul resurselor",
"SubTitle": "Resurse", "SubTitle": "Resurse",
@ -78,6 +85,10 @@
"CopyUrl": "Copiați adresa URL a resursei", "CopyUrl": "Copiați adresa URL a resursei",
"LinkCopiedToClipboard": "Linkul a fost copiat în clipboard." "LinkCopiedToClipboard": "Linkul a fost copiat în clipboard."
} }
},
"Links": {
"Title": "Legături",
"SubTitle": "Diferite forme ale URL-ului la care poate fi accesată o resursă"
} }
}, },
"ServerAvailability": { "ServerAvailability": {

View File

@ -0,0 +1,91 @@
import React from "react";
import PropTypes from "prop-types";
import { makeStyles } from "@material-ui/core/styles";
import {
Card,
CardHeader,
CardContent,
List,
ListItem,
ListItemText,
ListItemSecondaryAction,
ListItemIcon,
IconButton,
Link,
Tooltip
} from "@material-ui/core";
import style from "../styles";
import { LinkOutlined, FileCopyOutlined } from "@material-ui/icons";
import { useToast, useResourceSecurity } from "../../../../hooks";
import { useTranslation } from "react-i18next";
const useStyles = makeStyles(style);
const LinksComponent = ({ urls, secured }) => {
const classes = useStyles();
const { t } = useTranslation();
const { info } = useToast();
const { secureUrl } = useResourceSecurity();
const handleToggle = (url) => () => {
const urlMustBeSecured = secured || url.includes("id=");
const link = urlMustBeSecured ? secureUrl(url) : url;
navigator.clipboard.writeText(link);
info(t("Resource.List.Actions.LinkCopiedToClipboard"));
};
const preventDefault = (event) => event.preventDefault();
return (
<Card className={classes.linksCard}>
<CardHeader
className={classes.linksHeader}
title={t("Resource.Links.Title")}
subheader={t("Resource.Links.SubTitle")}
/>
<CardContent>
<List>
{urls.map((value, index) => {
return (
<ListItem
key={`url-${index}`}
dense
button
onClick={handleToggle(value)}
>
<ListItemIcon>
<LinkOutlined />
</ListItemIcon>
<ListItemText
primary={
<Link href={value} onClick={preventDefault}>
{value}
</Link>
}
/>
<ListItemSecondaryAction>
<Tooltip title={"Copy"}>
<IconButton
edge="end"
aria-label="comments"
size="small"
onClick={handleToggle(value)}
>
<FileCopyOutlined />
</IconButton>
</Tooltip>
</ListItemSecondaryAction>
</ListItem>
);
})}
</List>
</CardContent>
</Card>
);
};
LinksComponent.propTypes = {
urls: PropTypes.array.isRequired,
secured: PropTypes.bool.isRequired
};
export default LinksComponent;

View File

@ -2,12 +2,15 @@ import React, { useState } from "react";
import PropTypes from "prop-types"; import PropTypes from "prop-types";
import { Grid, TextField, FormControlLabel, Checkbox } from "@material-ui/core"; import { Grid, TextField, FormControlLabel, Checkbox } from "@material-ui/core";
import Autocomplete from "@material-ui/lab/Autocomplete"; import Autocomplete from "@material-ui/lab/Autocomplete";
import { useTranslation } from "react-i18next";
const MimeTypeComponent = ({ mimeType, mimeTypes, onPropertyChange }) => { const MimeTypeComponent = ({ mimeType, mimeTypes, onPropertyChange }) => {
const [isAutomaticMimeType, setIsAutomaticMimeType] = useState( const [isAutomaticMimeType, setIsAutomaticMimeType] = useState(
mimeType.mimeTypeId === null mimeType.mimeTypeId === null
); );
const { t } = useTranslation();
const handleChangeAutomaticMimeType = (event) => { const handleChangeAutomaticMimeType = (event) => {
const checked = event.target.checked; const checked = event.target.checked;
setIsAutomaticMimeType(checked); setIsAutomaticMimeType(checked);
@ -20,7 +23,7 @@ const MimeTypeComponent = ({ mimeType, mimeTypes, onPropertyChange }) => {
{isAutomaticMimeType ? ( {isAutomaticMimeType ? (
<TextField <TextField
id="automatic-resource-mime-type" id="automatic-resource-mime-type"
label="Automatic MIME type" label={t("Resource.AutomaticMimeType")}
fullWidth fullWidth
disabled disabled
value={mimeType.mimeTypeName} value={mimeType.mimeTypeName}
@ -43,7 +46,7 @@ const MimeTypeComponent = ({ mimeType, mimeTypes, onPropertyChange }) => {
}} }}
getOptionSelected={(option, value) => option.mimeTypeId === value} getOptionSelected={(option, value) => option.mimeTypeId === value}
renderInput={(params) => ( renderInput={(params) => (
<TextField {...params} label="Mime type" /> <TextField {...params} label={t("Resource.MimeType")} />
)} )}
/> />
)} )}
@ -58,7 +61,7 @@ const MimeTypeComponent = ({ mimeType, mimeTypes, onPropertyChange }) => {
color="primary" color="primary"
/> />
} }
label="Auto MIME type" label={t("Resource.AutomaticMimeType")}
/> />
</Grid> </Grid>
</> </>

View File

@ -18,6 +18,8 @@ import MimeTypeComponent from "./MimeTypeComponent";
import useResourceDeleteDialog from "../../hooks/useResourceDeleteDialog"; import useResourceDeleteDialog from "../../hooks/useResourceDeleteDialog";
import { onTextFieldChange } from "../../../../utils/adapters"; import { onTextFieldChange } from "../../../../utils/adapters";
import { useHistory } from "react-router-dom"; import { useHistory } from "react-router-dom";
import { useTranslation } from "react-i18next";
import LinksComponent from "./LinksComponent";
const useStyles = makeStyles(style); const useStyles = makeStyles(style);
@ -31,6 +33,7 @@ const ResourceComponent = ({
}) => { }) => {
const classes = useStyles(); const classes = useStyles();
const history = useHistory(); const history = useHistory();
const { t } = useTranslation();
const { const {
component: ResourceDeleteDialog, component: ResourceDeleteDialog,
handleOpen: handleOpenDeleteDialog handleOpen: handleOpenDeleteDialog
@ -40,7 +43,7 @@ const ResourceComponent = ({
return ( return (
<> <>
<Paper className={classes.paper}> <Paper className={classes.resourceData}>
<Grid container spacing={2}> <Grid container spacing={2}>
<Grid item xs={12} sm={5}> <Grid item xs={12} sm={5}>
<ButtonBase> <ButtonBase>
@ -52,7 +55,7 @@ const ResourceComponent = ({
<Grid item xs={12} sm={6}> <Grid item xs={12} sm={6}>
<TextField <TextField
id="resource-code" id="resource-code"
label="Code" label={t("Resource.Code")}
fullWidth fullWidth
required required
value={resource.resourceCode} value={resource.resourceCode}
@ -62,7 +65,7 @@ const ResourceComponent = ({
<Grid item xs={12} sm={6}> <Grid item xs={12} sm={6}>
<TextField <TextField
id="resource-name" id="resource-name"
label="Name" label={t("Resource.Name")}
fullWidth fullWidth
required required
value={resource.resourceName} value={resource.resourceName}
@ -72,7 +75,7 @@ const ResourceComponent = ({
<Grid item xs={12}> <Grid item xs={12}>
<TextField <TextField
id="resource-path" id="resource-path"
label="Path on disk" label={t("Resource.PathOnDisk")}
fullWidth fullWidth
disabled disabled
value={resource.resourcePath} value={resource.resourcePath}
@ -108,7 +111,7 @@ const ResourceComponent = ({
option.categoryId === value option.categoryId === value
} }
renderInput={(params) => ( renderInput={(params) => (
<TextField {...params} label="Category" /> <TextField {...params} label={t("Resource.Category")} />
)} )}
/> />
</Grid> </Grid>
@ -124,7 +127,7 @@ const ResourceComponent = ({
color="primary" color="primary"
/> />
} }
label="Secured" label={t("Resource.Secured")}
/> />
</Grid> </Grid>
</Grid> </Grid>
@ -135,7 +138,7 @@ const ResourceComponent = ({
multiline multiline
rows={12} rows={12}
id="resource-description" id="resource-description"
label="Description" label={t("Resource.Description")}
variant="outlined" variant="outlined"
/> />
</Grid> </Grid>
@ -149,7 +152,9 @@ const ResourceComponent = ({
onClick={handleOpenDeleteDialog} onClick={handleOpenDeleteDialog}
disabled={!!processing} disabled={!!processing}
> >
{processing === "delete" ? "Deleting" : "Delete"} {processing === "delete"
? t("Generic.Deleting")
: t("Generic.Delete")}
</Button> </Button>
<Button <Button
variant="outlined" variant="outlined"
@ -158,13 +163,16 @@ const ResourceComponent = ({
startIcon={<SaveIcon />} startIcon={<SaveIcon />}
disabled={!!processing} disabled={!!processing}
> >
{processing === "delete" ? "Saving" : "Save"} {processing === "delete"
? t("Generic.Saving")
: t("Generic.Save")}
</Button> </Button>
</Grid> </Grid>
</Grid> </Grid>
</Grid> </Grid>
</Paper> </Paper>
{ResourceDeleteDialog} {ResourceDeleteDialog}
<LinksComponent urls={resource.urls} secured={resource.secured} />
</> </>
); );
}; };

View File

@ -3,10 +3,16 @@ const style = (theme) => {
root: { root: {
flexGrow: 1 flexGrow: 1
}, },
paper: { resourceData: {
padding: theme.spacing(2), padding: theme.spacing(2),
margin: "auto" margin: "auto"
}, },
linksCard: {
marginTop: theme.spacing(2)
},
linksHeader: {
paddingBottom: 0
},
resourceImage: { resourceImage: {
margin: "auto", margin: "auto",
display: "block", display: "block",

View File

@ -93,7 +93,7 @@ const ResourcesContainer = () => {
} }
if (secure) { if (secure) {
url = secureUrl(url, download); url = secureUrl(url);
} }
return url; return url;

View File

@ -4,8 +4,8 @@ import { useCallback } from "react";
const useResourceSecurity = () => { const useResourceSecurity = () => {
const { token } = useUserState(); const { token } = useUserState();
const secureUrl = useCallback( const secureUrl = useCallback(
(url, join) => { (url) => {
const separator = join ? "&" : "?"; const separator = url.includes("?") ? "&" : "?";
const securedUrl = `${url}${separator}token=${token.raw}`; const securedUrl = `${url}${separator}token=${token.raw}`;
return securedUrl; return securedUrl;
}, },