Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/share-button #168

Merged
merged 14 commits into from
Jul 31, 2023
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions docker-compose-development.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ services:
- "GROUPID=${GROUPID:-1000}"
- "USERNAME=${USERNAME:-pzapp}"
command: /start.sh
user: "1000:1000"
env_file:
- .env
volumes:
Expand All @@ -31,6 +32,7 @@ services:

frontend:
image: node:lts
user: "1000:1000"
working_dir: /app
volumes:
- ./frontend:/app
Expand Down
45 changes: 36 additions & 9 deletions frontend/components/Header.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,28 @@ import {
Menu,
MenuItem,
Toolbar,
Typography
Typography,
ListItemIcon
} from '@mui/material'
import { useRouter } from 'next/router'
import React from 'react'

import { GitHub, Twitter, YouTube } from '@mui/icons-material'
import React from 'react'
import PropTypes from 'prop-types'
import {
GitHub,
Twitter,
YouTube,
Brightness4,
Brightness7,
Logout
} from '@mui/icons-material'
import MoreIcon from '@mui/icons-material/MoreVert'
import TokenDialog from '../components/TokenDialog'
import { useAuth } from '../contexts/AuthContext'
import useStyles from '../styles/components/Header'
import Link from './Link'

function Header() {
function Header({ darkMode, setDarkMode }) {
const classes = useStyles()
const router = useRouter()
const { user, logout } = useAuth()
Expand Down Expand Up @@ -55,16 +64,18 @@ function Header() {
}

const handleTokenOpen = () => {
// Fecha o menu
setAnchorEl(null)
// Abre a Dialog Token Api
setOpen(true)
}

const handleTokenClose = () => {
setOpen(false)
}

const handleToggleDarkMode = () => {
setDarkMode(prevMode => !prevMode)
}

return (
<div>
<AppBar position="static" className={classes.appbar}>
Expand All @@ -80,6 +91,14 @@ function Header() {
</List>
<div className={classes.separator} />
<Typography>{user?.username}</Typography>
<IconButton
size="large"
edge="end"
color="inherit"
onClick={handleToggleDarkMode}
>
{darkMode ? <Brightness7 /> : <Brightness4 />}
</IconButton>
<IconButton
size="large"
edge="end"
Expand All @@ -104,11 +123,15 @@ function Header() {
onClose={handleClose}
>
<MenuItem onClick={handleTokenOpen}>API Token</MenuItem>
<MenuItem onClick={logout}>Logout</MenuItem>
<MenuItem onClick={logout}>
<ListItemIcon>
<Logout fontSize="small" />
</ListItemIcon>
<Typography variant="inherit">Logout</Typography>
</MenuItem>
</Menu>
</Toolbar>
</AppBar>

{router.pathname === '/' && (
<Grid className={classes.banner}>
<Grid
Expand All @@ -119,7 +142,6 @@ function Header() {
className={classes.container}
>
<Grid item xs={12} className={classes.titleWrapper}>
{/* <h1 className={classes.title}>Photo-z Server</h1> */}
<Typography variant="h1" className={classes.title}>
Photo-z Server
</Typography>
Expand Down Expand Up @@ -181,4 +203,9 @@ function Header() {
)
}

Header.propTypes = {
darkMode: PropTypes.bool.isRequired,
setDarkMode: PropTypes.func.isRequired
}

export default Header
98 changes: 84 additions & 14 deletions frontend/components/ProductDetail.js
Original file line number Diff line number Diff line change
@@ -1,20 +1,32 @@
import VerifiedIcon from '@mui/icons-material/Verified'
import LoadingButton from '@mui/lab/LoadingButton'
import {
Button,
IconButton,
Snackbar,
Box,
Card,
CardContent,
CardHeader,
Chip,
Dialog,
DialogContent,
DialogTitle,
Divider,
Grid,
List,
ListItem,
ListItemText,
Paper,
Stack,
TextField,
Typography
} from '@mui/material'
import DialogActions from '@mui/material/DialogActions'
import InputAdornment from '@mui/material/InputAdornment'
import ShareIcon from '@mui/icons-material/Share'
import Alert from '@mui/material/Alert'

import moment from 'moment'
import prettyBytes from 'pretty-bytes'
import PropTypes from 'prop-types'
Expand All @@ -38,6 +50,8 @@ export default function ProductDetail({ productId, internalName }) {
const [isLoading, setLoading] = React.useState(false)
const [notFound, setNotFound] = React.useState(false)
const [isDownloading, setDownloading] = React.useState(false)
const [shareDialogOpen, setShareDialogOpen] = React.useState(false)
const [shareUrl, setShareUrl] = React.useState('')

const loadProductById = React.useCallback(async () => {
setLoading(true)
Expand Down Expand Up @@ -122,7 +136,6 @@ export default function ProductDetail({ productId, internalName }) {
}
})
}, [product])

React.useEffect(() => {
if (product) {
loadFiles()
Expand Down Expand Up @@ -157,15 +170,7 @@ export default function ProductDetail({ productId, internalName }) {
name = file.name.substring(0, 23) + '...' + extension
}
return (
<ListItem
key={`file_${file.id}`}
disableGutters
// secondaryAction={
// <IconButton component={Link} href={file.file} target="_blank">
// <DownloadIcon />
// </IconButton>
// }
>
<ListItem key={`file_${file.id}`} disableGutters>
{file.role === 0 && (
<ListItemText
primary={name}
Expand All @@ -179,13 +184,46 @@ export default function ProductDetail({ productId, internalName }) {
)
}

const [snackbarOpen, setSnackbarOpen] = React.useState(false)

const showSnackbar = () => {
setSnackbarOpen(true)
}

const handleShareDialogOpen = () => {
setShareDialogOpen(true)
setShareUrl(window.location.href)
}

const handleShareDialogClose = () => {
setShareDialogOpen(false)
}

const handleCopyUrl = () => {
navigator.clipboard.writeText(shareUrl)
showSnackbar()
}

if (isLoading) return <Loading isLoading={isLoading} />
if (notFound) return <ProductNotFound />
if (!product) return null

return (
<React.Fragment>
<Box className={classes.pageHeader}>
<Snackbar
open={snackbarOpen}
autoHideDuration={3000}
onClose={() => setSnackbarOpen(false)}
>
<Alert
onClose={() => setSnackbarOpen(false)}
severity="success"
sx={{ width: '100%' }}
>
Link copied successfully!
</Alert>
</Snackbar>
<Typography variant="h6">Product</Typography>
</Box>
<Box component="form" noValidate autoComplete="off">
Expand All @@ -200,12 +238,14 @@ export default function ProductDetail({ productId, internalName }) {
<Paper elevation={2} className={classes.paper}>
<Stack
direction="row"
justifyContent="space-between"
alignItems="flex-start"
justifyContent="flex-start"
alignItems="center"
spacing={2}
>
<Typography variant="h4">{product.display_name}</Typography>

<IconButton onClick={handleShareDialogOpen}>
<ShareIcon />
</IconButton>
{product.official_product === true && (
<Chip
variant="outlined"
Expand Down Expand Up @@ -234,8 +274,38 @@ export default function ProductDetail({ productId, internalName }) {
{product.release_name} - {product.product_type_name}
</Typography>
{product.description !== '' && (
<Typography variant="body">{product.description}</Typography>
<Typography variant="body1">{product.description}</Typography>
)}
<Dialog
open={shareDialogOpen}
onClose={handleShareDialogClose}
PaperProps={{
style: { width: '500px', minHeight: '150px' }
}}
>
<DialogTitle style={{ fontSize: '16px' }}>
Copy the download URL:
</DialogTitle>
<DialogContent>
<TextField
fullWidth
variant="outlined"
value={shareUrl}
InputProps={{
endAdornment: (
<InputAdornment position="end">
<Button variant="contained" onClick={handleCopyUrl}>
Copy
</Button>
</InputAdornment>
)
}}
/>
</DialogContent>
<DialogActions>
<Button onClick={handleShareDialogClose}>Close</Button>
</DialogActions>
</Dialog>
</Paper>
</Grid>
<Grid item xs={4}>
Expand Down
Loading