feat: added pages for viewing artist access requests, users, and artists in the admin menus

This commit is contained in:
Damien Ostler 2024-03-17 06:47:37 -04:00
parent 23ea8eed79
commit 2a405067d8
12 changed files with 284 additions and 32 deletions

View File

@ -10,8 +10,7 @@ import Typography from '@mui/material/Typography';
import MenuItem from '@mui/material/MenuItem'; import MenuItem from '@mui/material/MenuItem';
import Drawer from '@mui/material/Drawer'; import Drawer from '@mui/material/Drawer';
import MenuIcon from '@mui/icons-material/Menu'; import MenuIcon from '@mui/icons-material/Menu';
import ToggleColorMode from './ToggleColorMode'; import { Logout, OpenInNew } from '@mui/icons-material';
import { ArrowLeftOutlined, ArrowRightRounded, Logout, OpenInNew } from '@mui/icons-material';
const logoStyle = { const logoStyle = {
width: '140px', width: '140px',

View File

@ -4,10 +4,6 @@ import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider';
import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs'; import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs';
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
import { Rating } from '@mui/material'; import { Rating } from '@mui/material';
import dayjs from 'dayjs';
import CurrencyTextField from '@lupus-ai/mui-currency-textfield';
import { DateField } from '@mui/x-date-pickers';
export default function Reviews({artistId}) { export default function Reviews({artistId}) {
const router = useRouter(); const router = useRouter();
@ -30,15 +26,19 @@ export default function Reviews({artistId}) {
const getReviews = async () => { const getReviews = async () => {
setIsLoading(true); setIsLoading(true);
const response = await fetch('/api/discovery/artist/'+artistId+'/reviews', { const response = await fetch('/api/discovery/artist/'+artistId+'/reviews', {
method: 'GET', method: 'GET',
headers: { headers: {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
} }
}); });
const data = await response.json(); const data = await response.json();
setReviewData(data); // Assuming your API returns an array under a key like 'reviews'
// Adjust this according to your actual API response structure
const rows = data.reviews || []; // If 'reviews' doesn't exist, default to an empty array
setReviewData(rows);
setIsLoading(false); setIsLoading(false);
} }
const getReviewsCount = async () => { const getReviewsCount = async () => {
const response = await fetch('/api/discovery/artist/'+artistId+'/reviewscount', { const response = await fetch('/api/discovery/artist/'+artistId+'/reviewscount', {
method: 'GET', method: 'GET',

0
components/test.tsx Normal file
View File

View File

@ -3,7 +3,7 @@ import { getAccessToken, withApiAuthRequired, getSession } from '@auth0/nextjs-a
export default withApiAuthRequired(async function handler(req, res) { export default withApiAuthRequired(async function handler(req, res) {
const { accessToken } = await getAccessToken(req, res); const { accessToken } = await getAccessToken(req, res);
const { artistId } = req.query; const { artistId } = req.query;
const response = await fetch(process.env.NEXT_PUBLIC_API_URL+'/api/admin/AdminArtists/'+userId, { const response = await fetch(process.env.NEXT_PUBLIC_API_URL+'/api/admin/AdminArtists/'+artistId, {
headers: { headers: {
"Authorization": `Bearer ${accessToken}` "Authorization": `Bearer ${accessToken}`
} }

View File

@ -9,10 +9,7 @@ export default withApiAuthRequired(async function handler(req, res) {
}, },
method: req.method method: req.method
}); });
if(response.ok==false){ console.log(response)
res.status(200).json({})
return;
}
let result = await response.json(); let result = await response.json();
res.status(200).json(result); res.status(200).json(result);
}); });

View File

@ -0,0 +1,16 @@
import { getAccessToken, withApiAuthRequired, getSession } from '@auth0/nextjs-auth0';
export default withApiAuthRequired(async function products(req, res) {
const { accessToken } = await getAccessToken(req, res);
const response = await fetch(process.env.NEXT_PUBLIC_API_URL+'/api/Artist', {
headers: {
"Authorization": `Bearer ${accessToken}`,
'Content-Type': 'application/json'
},
method: 'POST',
body: req.body
});
let result = await response.json();
res.status(200).json(result);
});

View File

@ -65,7 +65,7 @@ export default function AdminArtists() {
} }
}}, }},
{ field: 'actions', headerName: '', flex: 0.05, sortable: false, filterable: false, renderCell: (params) => { { field: 'actions', headerName: '', flex: 0.05, sortable: false, filterable: false, renderCell: (params) => {
return <Tooltip title="View more information about this user."><IconButton color="info" onClick={() => router.push("/dashboard/admin/users/"+params.row.id)}><OpenInNew /></IconButton></Tooltip> return <Tooltip title="View more information about this user."><IconButton color="info" onClick={() => router.push("/dashboard/admin/artists/"+params.row.id)}><OpenInNew /></IconButton></Tooltip>
}} }}
]; ];
} }

View File

@ -2,13 +2,23 @@ import * as React from 'react';
import { useState, useEffect } from 'react'; import { useState, useEffect } from 'react';
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
import { withPageAuthRequired } from '@auth0/nextjs-auth0/client' import { withPageAuthRequired } from '@auth0/nextjs-auth0/client'
import { Alert, Card, CardContent, Grid, IconButton, TextField, Typography } from '@mui/material';
import { ArrowBack, ArrowLeft, Block, Close } from '@mui/icons-material';
import Button from '@mui/material/Button';
import { OpenInNew } from 'mdi-material-ui';
import Tooltip from '@mui/material/Tooltip';
const AdminArtist = () => { const AdminArtist = () => {
const router = useRouter(); const router = useRouter();
const [artist, setArtist] = useState(null);
const getData = async () => { const getData = async () => {
if(router.query.artistId!=null){ if(router.query.artistId!=null){
const response = await fetch("/api/admin/artists/"+router.query.artistId);
const data = await response.json();
setArtist(data);
} }
} }
@ -17,12 +27,78 @@ const AdminArtist = () => {
getData() getData()
}, [router.query.artistId]); }, [router.query.artistId]);
return (<> return (
</> <>
<Card>
<CardContent>
<Grid container spacing={3}>
<Grid item xs={6}>
<Typography variant="h5">Artist Information</Typography>
</Grid>
<Grid item xs={6} sx={{textAlign:"right"}}>
<Tooltip title="Ban this artist.">
<IconButton color="error">
<Block/>
</IconButton>
</Tooltip>
<Tooltip title="Suspend this artist.">
<IconButton color="warning">
<Close/>
</IconButton>
</Tooltip>
<Tooltip title="Go back a page.">
<IconButton onClick={() => router.push("/dashboard/admin/artists")} color="primary">
<ArrowBack/>
</IconButton>
</Tooltip>
</Grid>
<Grid item xs={12} md={6}>
<Grid container spacing={3}>
<Grid item xs={12} md={5}>
<Grid container spacing={3}>
<Grid item xs={12}>
Display Name: {artist?.user?.displayName}
</Grid>
<Grid item xs={12}>
Email: {artist?.user?.email}
</Grid>
<Grid item xs={12}>
<Button variant="contained" color="primary" fullWidth>Save Changes</Button>
</Grid>
</Grid>
</Grid>
<Grid item xs={12} md={7}>
<TextField size="small" label="Admin Notes" multiline rows={4} fullWidth variant="outlined" value={artist?.userId} />
</Grid>
<Grid item xs={12}>
<Alert severity={artist?.numberOfBans > 0 ? "error" : "success"}>{artist?.numberOfBans > 0 ? "This user has been banned "+artist?.numberOfBans+" times." : "This user has not been banned before."}</Alert>
</Grid>
<Grid item xs={12}>
<Alert severity={artist?.numberOfSuspensions > 0 ? "error" : "success"}>{artist?.numberOfBans > 0 ? "This user has been suspended "+artist?.numberOfSuspensions+" times." : "This user has not been suspended before."}</Alert>
</Grid>
</Grid>
</Grid>
<Grid item xs={12} md={6}>
<Grid container spacing={3}>
<Grid item xs={12}>
<Alert severity="success">This artist has made ${artist?.amountMade}, and we have made ${artist?.feesCollected} in fees.</Alert>
</Grid>
<Grid item xs={12}>
<Alert severity={artist?.numberOfRequests > 0 ? "success" : "warning"}>This artist has accepted {artist?.numberOfRequests} requests.</Alert>
</Grid>
<Grid item xs={12}>
<Alert severity={artist?.numberOfCompleted > 0 ? "success" : "warning"}>This artist has completed {artist?.numberOfCompleted} requests.</Alert>
</Grid>
</Grid>
</Grid>
</Grid>
</CardContent>
</Card>
</>
); );
}; };
// Protected route, checking user authentication client-side.(CSR) // Protected route, checking user authentication client-side.(CSR)
export default withPageAuthRequired(ArtistRequestDetails); export default withPageAuthRequired(AdminArtist);

View File

@ -2,22 +2,110 @@ import * as React from 'react';
import { useState, useEffect } from 'react'; import { useState, useEffect } from 'react';
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
import { withPageAuthRequired } from '@auth0/nextjs-auth0/client' import { withPageAuthRequired } from '@auth0/nextjs-auth0/client'
import { Alert, Card, CardContent, Grid, IconButton, TextField, Typography } from '@mui/material';
import Button from '@mui/material/Button';
import { OpenInNew } from 'mdi-material-ui';
import Tooltip from '@mui/material/Tooltip';
import { ArrowBack } from '@mui/icons-material';
const AdminRequest = () => { const AdminRequest = () => {
const router = useRouter(); const router = useRouter();
const [request, setRequest] = useState(null);
const getData = async () => { const getData = async () => {
if(router.query.artistId!=null){ if(router.query.requestId!=null){
const response = await fetch("/api/admin/requests/"+router.query.requestId);
const data = await response.json();
setRequest(data);
} }
} }
const handleAccept = async () => {
var response = await fetch("/api/admin/requests/"+router.query.requestId, {method:"PUT"})
if(response.ok){
var data = await response.json();
router.reload();
}
else{
alert("Failed to accept request.")
}
}
const handleDeny = async () => {
var response = await fetch("/api/admin/requests/"+router.query.requestId, {method:"DELETE"})
if(response.ok){
var data = await response.json();
router.reload();
}
else{
alert("Failed to deny request.")
}
}
useEffect(() => { useEffect(() => {
getData() getData()
}, [router.query.artistId]); }, [router.query.requestId]);
return (<> let formattedTime = ""
const date = new Date(request?.requestDate);
formattedTime = date.toLocaleTimeString('en-US', { month: 'long', day: '2-digit', year: 'numeric', hour: '2-digit', minute: '2-digit' }); // Example format
return (
<>
<Card>
<CardContent>
<Grid container>
<Grid item xs={6}>
<Typography variant="h5">{router.query.requestId} - Artist Access Request</Typography>
</Grid>
<Grid item xs={6} sx={{textAlign:"right"}}>
<Tooltip title="Go back a page.">
<IconButton onClick={() => router.push("/dashboard/admin/requests")} color="primary">
<ArrowBack/>
</IconButton>
</Tooltip>
</Grid>
<Grid item xs={6}>
<Grid container spacing={3}>
<Grid item xs={12}>
<Alert severity="info">Submitted on {formattedTime}</Alert>
</Grid>
{request?.accepted ? (
<Grid item xs={12}>
<Alert severity="success">This artist access request has been accepted.</Alert>
</Grid>
):(
<Grid item xs={12}>
<Alert severity="warning">Pending review from platform administrator.</Alert>
</Grid>
)}
<Grid item xs={12}>
<TextField
id="outlined-multiline-static"
label="Message"
multiline
rows={4}
fullWidth
value={request?.message}
disabled>
{request?.message}
</TextField>
</Grid>
<Grid item xs={12} md={6}>
<Button fullWidth disabled={request?.accepted} variant="contained" onClick={handleAccept} color="primary">Accept</Button>
</Grid>
<Grid item xs={12} md={6}>
<Button fullWidth disabled={request?.accepted} variant="contained" onClick={handleDeny} color="secondary">Reject</Button>
</Grid>
</Grid>
</Grid>
<Grid item xs={6}>
</Grid>
</Grid>
</CardContent>
</Card>
</> </>
); );
}; };

View File

@ -45,8 +45,8 @@ export default function AdminUsers() {
{ field: 'id', headerName: 'User ID', flex: 0.2, sortable: false, filterable: false}, { field: 'id', headerName: 'User ID', flex: 0.2, sortable: false, filterable: false},
{ field: 'displayName', headerName: 'Display Name', flex: 0.15, sortable: false, filterable: false}, { field: 'displayName', headerName: 'Display Name', flex: 0.15, sortable: false, filterable: false},
{ field: 'email', headerName: 'Email', flex: 0.2, sortable: false, filterable: false}, { field: 'email', headerName: 'Email', flex: 0.2, sortable: false, filterable: false},
{ field: "requestCount", headerName: "# of Requests", flex: 0.1, sortable: false, filterable: false}, { field: "numberOfRequests", headerName: "# of Requests", flex: 0.1, sortable: false, filterable: false},
{ field: "reviewCount", headerName: "# of Reviews", flex: 0.1, sortable: false, filterable: false}, { field: "numberOfReviews", headerName: "# of Reviews", flex: 0.1, sortable: false, filterable: false},
{ field: "amountSpent", headerName: "Amount Spent", flex: 0.1, sortable: false, filterable: false}, { field: "amountSpent", headerName: "Amount Spent", flex: 0.1, sortable: false, filterable: false},
{ field: 'status', headerName: 'Status', flex: 0.1, sortable: false, filterable: false, renderCell: (params) =>{ { field: 'status', headerName: 'Status', flex: 0.1, sortable: false, filterable: false, renderCell: (params) =>{
if(params.row.banned){ if(params.row.banned){

View File

@ -2,23 +2,99 @@ import * as React from 'react';
import { useState, useEffect } from 'react'; import { useState, useEffect } from 'react';
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
import { withPageAuthRequired } from '@auth0/nextjs-auth0/client' import { withPageAuthRequired } from '@auth0/nextjs-auth0/client'
import { Alert, Card, CardContent, Grid, IconButton, TextField, Typography } from '@mui/material';
import { ArrowBack, ArrowLeft, Block, Close } from '@mui/icons-material';
import Button from '@mui/material/Button';
import { OpenInNew } from 'mdi-material-ui';
import Tooltip from '@mui/material/Tooltip';
const AdminUser = () => { const AdminUser = () => {
const router = useRouter(); const router = useRouter();
const [user, setUser] = useState(null);
const getData = async () => { const getData = async () => {
if(router.query.artistId!=null){ if(router.query.userId!=null){
const response = await fetch("/api/admin/users/"+router.query.userId);
const data = await response.json();
setUser(data);
} }
} }
useEffect(() => { useEffect(() => {
getData() getData()
}, [router.query.artistId]); }, [router.query.userId]);
return (<> return (
</> <>
<Card>
<CardContent>
<Grid container spacing={3}>
<Grid item xs={6}>
<Typography variant="h5">User Information</Typography>
</Grid>
<Grid item xs={6} sx={{textAlign:"right"}}>
<Tooltip title="Ban this user.">
<IconButton color="error">
<Block/>
</IconButton>
</Tooltip>
<Tooltip title="Suspend this user.">
<IconButton color="warning">
<Close/>
</IconButton>
</Tooltip>
<Tooltip title="Go back a page.">
<IconButton onClick={() => router.push("/dashboard/admin/users")} color="primary">
<ArrowBack/>
</IconButton>
</Tooltip>
</Grid>
<Grid item xs={12} md={6}>
<Grid container spacing={3}>
<Grid item xs={12} md={5}>
<Grid container spacing={3}>
<Grid item xs={12}>
Display Name: {user?.displayName}
</Grid>
<Grid item xs={12}>
Email: {user?.email}
</Grid>
<Grid item xs={12}>
<Button variant="contained" color="primary" fullWidth>Save Changes</Button>
</Grid>
</Grid>
</Grid>
<Grid item xs={12} md={7}>
<TextField size="small" label="Admin Notes" multiline rows={4} fullWidth variant="outlined" value={user?.userId} />
</Grid>
<Grid item xs={12}>
<Alert severity={user?.numberOfBans > 0 ? "error" : "success"}>{user?.numberOfBans > 0 ? "This user has been banned "+user?.numberOfBans+" times." : "This user has not been banned before."}</Alert>
</Grid>
<Grid item xs={12}>
<Alert severity={user?.numberOfSuspensions > 0 ? "error" : "success"}>{user?.numberOfBans > 0 ? "This user has been suspended "+user?.numberOfSuspensions+" times." : "This user has not been suspended before."}</Alert>
</Grid>
</Grid>
</Grid>
<Grid item xs={12} md={6}>
<Grid container spacing={3}>
<Grid item xs={12}>
<Alert severity={user?.numberOfRequests > 0 ? "success" : "warning"}>This user has opened {user?.numberOfRequests} requests.</Alert>
</Grid>
<Grid item xs={12}>
<Alert severity={user?.amountSpent > 0 ? "success" : "warning"}>This user has paid {user?.amountSpent} for {user?.numberOfPaid} requests.</Alert>
</Grid>
<Grid item xs={12}>
<Alert severity={user?.numberOfReviews > 0 ? "success" : "warning"}>This user has left {user?.numberOfReviews} reviews on completed requests.</Alert>
</Grid>
</Grid>
</Grid>
</Grid>
</CardContent>
</Card>
</>
); );
}; };