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 Drawer from '@mui/material/Drawer';
import MenuIcon from '@mui/icons-material/Menu';
import ToggleColorMode from './ToggleColorMode';
import { ArrowLeftOutlined, ArrowRightRounded, Logout, OpenInNew } from '@mui/icons-material';
import { Logout, OpenInNew } from '@mui/icons-material';
const logoStyle = {
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 { useRouter } from 'next/router';
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}) {
const router = useRouter();
@ -30,15 +26,19 @@ export default function Reviews({artistId}) {
const getReviews = async () => {
setIsLoading(true);
const response = await fetch('/api/discovery/artist/'+artistId+'/reviews', {
method: 'GET',
headers: {
'Content-Type': 'application/json',
}
method: 'GET',
headers: {
'Content-Type': 'application/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);
}
const getReviewsCount = async () => {
const response = await fetch('/api/discovery/artist/'+artistId+'/reviewscount', {
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) {
const { accessToken } = await getAccessToken(req, res);
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: {
"Authorization": `Bearer ${accessToken}`
}

View File

@ -9,10 +9,7 @@ export default withApiAuthRequired(async function handler(req, res) {
},
method: req.method
});
if(response.ok==false){
res.status(200).json({})
return;
}
console.log(response)
let result = await response.json();
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) => {
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 { useRouter } from 'next/router';
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 router = useRouter();
const [artist, setArtist] = useState(null);
const getData = async () => {
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()
}, [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)
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 { useRouter } from 'next/router';
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 router = useRouter();
const getData = async () => {
if(router.query.artistId!=null){
const [request, setRequest] = useState(null);
const getData = async () => {
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(() => {
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: 'displayName', headerName: 'Display Name', flex: 0.15, 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: "reviewCount", headerName: "# of Reviews", flex: 0.1, sortable: false, filterable: false},
{ field: "numberOfRequests", headerName: "# of Requests", 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: 'status', headerName: 'Status', flex: 0.1, sortable: false, filterable: false, renderCell: (params) =>{
if(params.row.banned){

View File

@ -2,23 +2,99 @@ import * as React from 'react';
import { useState, useEffect } from 'react';
import { useRouter } from 'next/router';
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 router = useRouter();
const getData = async () => {
if(router.query.artistId!=null){
const [user, setUser] = useState(null);
const getData = async () => {
if(router.query.userId!=null){
const response = await fetch("/api/admin/users/"+router.query.userId);
const data = await response.json();
setUser(data);
}
}
useEffect(() => {
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>
</>
);
};