Compare commits

...

12 Commits
0.7.0 ... main

Author SHA1 Message Date
01cc7eb197 Revert "dockerfile"
This reverts commit 2d4663ef7416ea2f3efe808f0cfca9a0557f0dc3.
2024-05-21 22:35:19 -04:00
2d4663ef74 dockerfile 2024-05-21 22:35:13 -04:00
Damien Ostler
0807d6d11d fix: removed logo that is broken 2024-03-17 06:56:35 -04:00
Damien Ostler
2a405067d8 feat: added pages for viewing artist access requests, users, and artists in the admin menus 2024-03-17 06:47:37 -04:00
Damien Ostler
23ea8eed79 fix: admin menus dont show up for non admins now 2024-03-03 16:23:40 -05:00
Damien Ostler
8569290c0e chore: cleaned up the naming of components, apis, and pgaes. 2024-03-03 16:19:47 -05:00
Damien Ostler
5a2f43768d feat: admin list pages and apis 2024-03-03 16:03:22 -05:00
Damien Ostler
ff465278ad fix: filtering and sorting disabled now 2024-03-03 14:11:43 -05:00
Damien Ostler
a63d7f35b8 fix: upload button enabled when not paid 2024-03-02 01:23:02 -05:00
Damien Ostler
93d4518500 feat: refactoring + assets and reference images and reviews 2024-03-02 00:51:43 -05:00
Damien Ostler
d52018f7eb removed login override 2024-02-28 21:28:30 -05:00
Damien Ostler
aac4ec902f feat: rating/reviews 2024-02-27 21:37:43 -05:00
105 changed files with 3256 additions and 2806 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

@ -85,7 +85,7 @@ const ArtistStats = ({profileData, stats}) => {
/>
<CardContent sx={{ pt: theme => `${theme.spacing(3)} !important` }}>
<Grid container spacing={[5, 0]}>
<Grid item xs={12} sm={3}>
<Grid item xs={6} sm={3}>
<Box sx={{ display: 'flex', alignItems: 'center' }}>
<Avatar
variant='rounded'
@ -106,7 +106,7 @@ const ArtistStats = ({profileData, stats}) => {
</Box>
</Box>
</Grid>
<Grid item xs={12} sm={3}>
<Grid item xs={6} sm={3}>
<Box sx={{ display: 'flex', alignItems: 'center' }}>
<Avatar
variant='rounded'
@ -127,7 +127,7 @@ const ArtistStats = ({profileData, stats}) => {
</Box>
</Box>
</Grid>
<Grid item xs={12} sm={3}>
<Grid item xs={6} sm={3}>
<Box sx={{ display: 'flex', alignItems: 'center' }}>
<Avatar
variant='rounded'
@ -148,7 +148,7 @@ const ArtistStats = ({profileData, stats}) => {
</Box>
</Box>
</Grid>
<Grid item xs={12} sm={3}>
<Grid item xs={6} sm={3}>
<Box sx={{ display: 'flex', alignItems: 'center' }}>
<Avatar
variant='rounded'

View File

@ -1,77 +0,0 @@
import * as React from 'react';
import Card from '@mui/material/Card';
import CardActions from '@mui/material/CardActions';
import CardContent from '@mui/material/CardContent';
import Typography from '@mui/material/Typography';
import CardMedia from '@mui/material/CardMedia';
import StarBorderOutlinedIcon from '@mui/icons-material/StarBorderOutlined';
import ShoppingCartCheckoutOutlinedIcon from '@mui/icons-material/ShoppingCartCheckoutOutlined';
import AccountCircleOutlinedIcon from '@mui/icons-material/AccountCircleOutlined';
import Grid from '@mui/material/Grid';
import Item from '@mui/material/Grid';
import Accordion from '@mui/material/Accordion';
import AccordionSummary from '@mui/material/AccordionSummary';
import AccordionDetails from '@mui/material/AccordionDetails';
import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
import ArtistPortfolio from './artistPortfolio';
import Button from '@mui/material/Button';
import Tooltip from '@mui/material/Tooltip';
import { useEffect, useState } from "react";
import { IconButton } from '@mui/material';
const Artist = ({user, artistId}) => {
const [sellerData, setArtistData] = useState([]);
useEffect(() => {
const getData = async () => {
const response = await fetch('/api/discovery/artist/'+artistId);
const data = await response.json();
setArtistData(data);
}
getData();
}, []);
return (
<Card color="primary" sx={{margin:5}}>
<CardContent>
<Grid container spacing={2}>
<Grid item xs={6} md={8}>
<Item>
<Typography variant="h5" component="h2">
{sellerData["name"]}
</Typography>
<Typography color="primary">
{sellerData["averageRating"] ? `${sellerData["averageRating"]} Stars (${sellerData["reviewCount"]} Reviews)` : "No Reviews"}
</Typography>
<Typography variant="body2" component="p">
{sellerData["biography"]}
</Typography>
</Item>
</Grid>
<Grid item xs={6} md={4}>
<Grid item xs={6} md={4}>
<Button href={"artist/"+artistId} color="primary" variant='contained' sx={{width:160}}>View Profile</Button>
{user ? (
<Button color="secondary" variant='contained' href={"/artist/"+artistId+"/request"} sx={{ width: 160, marginTop:2 }}>Submit Request</Button>
) : (
<Tooltip title="Log in order to place a request.">
<span>
<Button disabled color="secondary" variant='contained' sx={{ width: 160, marginTop:2 }}>Submit Request</Button>
</span>
</Tooltip>
)}
</Grid>
</Grid>
<Grid item xs={12} md={12}>
<Item>
</Item>
</Grid>
</Grid>
</CardContent>
</Card>)
}
export default Artist

View File

@ -1,28 +0,0 @@
import * as React from 'react';
import ImageList from '@mui/material/ImageList';
import ImageListItem from '@mui/material/ImageListItem';
import { useEffect, useState } from "react";
import { CircularProgress } from '@mui/material';
import { IconButton } from '@mui/material';
const ArtistPortfolioImage = ({artistId,itemId}) => {
const [loaded, setLoaded] = useState(false);
const handleImageLoaded = () => {
setLoaded(true);
};
return (
<ImageListItem key={itemId } >
<img
srcSet={process.env.NEXT_PUBLIC_API_URL+`/api/Discovery/Artists/${artistId}/Portfolio/${itemId}`}
src={process.env.NEXT_PUBLIC_API_URL+`/api/Discovery/Artists/${artistId}/Portfolio/${itemId}`}
alt={itemId}
loading="lazy"
style={{ filter: loaded ? 'blur(0)' : 'blur(10px)', backgroundColor:'grey' }}
onLoad={handleImageLoaded}
/>
</ImageListItem>)
}
export default ArtistPortfolioImage

View File

@ -1,48 +0,0 @@
import * as React from 'react';
import ImageList from '@mui/material/ImageList';
import ImageListItem from '@mui/material/ImageListItem';
import { useEffect, useState } from "react";
import DeleteIcon from '@mui/icons-material/Delete';
import { CircularProgress, ImageListItemBar } from '@mui/material';
import { IconButton } from '@mui/material';
const EditableArtistPortfolioImage = ({artistId,itemId,reload}) => {
const [loaded, setLoaded] = useState(false);
const [deleting, setDeleting] = useState(false);
const handleImageLoaded = () => {
setLoaded(true);
};
const deleteButton = () => {
setDeleting(true);
fetch('/api/artist/portfolio/'+itemId+"/delete", {
method: 'DELETE'
}).then(response => {
reload().then(data => {
})
})
}
return (
<ImageListItem key={itemId }>
<img
srcSet={process.env.NEXT_PUBLIC_API_URL+`/api/Discovery/Artists/${artistId}/Portfolio/${itemId}`}
src={process.env.NEXT_PUBLIC_API_URL+`/api/Discovery/Artists/${artistId}/Portfolio/${itemId}`}
alt={itemId}
loading="lazy"
style={{ filter: loaded ? 'blur(0)' : 'blur(10px)', backgroundColor:'grey' }}
onLoad={handleImageLoaded}
/>
<ImageListItemBar
actionIcon={
<IconButton onClick={deleteButton} color="error" >
<DeleteIcon />
</IconButton>
}>
</ImageListItemBar>
</ImageListItem>)
}
export default EditableArtistPortfolioImage

View File

@ -1,139 +0,0 @@
import * as React from 'react';
import { useUser } from "@auth0/nextjs-auth0/client";
import AppBar from '@mui/material/AppBar';
import Box from '@mui/material/Box';
import Toolbar from '@mui/material/Toolbar';
import Typography from '@mui/material/Typography';
import Menu from '@mui/material/Menu';
import Container from '@mui/material/Container';
import Button from '@mui/material/Button';
import MenuItem from '@mui/material/MenuItem';
import { Chip, Icon } from '@mui/material';
import {
NovuProvider,
PopoverNotificationCenter,
NotificationBell,
} from '@novu/notification-center';
type HeaderProps = {
user?: any;
loading: boolean;
};
const settings = ['Artist Dashboard', 'Account', 'Dashboard', 'Logout'];
function ResponsiveAppBar() {
const [anchorElNav, setAnchorElNav] = React.useState<null | HTMLElement>(null);
const [anchorElUser, setAnchorElUser] = React.useState<null | HTMLElement>(null);
const handleOpenNavMenu = (event: React.MouseEvent<HTMLElement>) => {
setAnchorElNav(event.currentTarget);
};
const handleOpenUserMenu = (event: React.MouseEvent<HTMLElement>) => {
setAnchorElUser(event.currentTarget);
};
const handleCloseNavMenu = () => {
setAnchorElNav(null);
};
const handleCloseUserMenu = () => {
setAnchorElUser(null);
};
const { user, isLoading } = useUser();
return (
<AppBar color="primary" position="static">
<Container maxWidth="xl">
<Toolbar disableGutters>
<Box sx={{ flex:1, textAlign:"center" }}>
<Typography
href="/"
variant="h6"
noWrap
color="secondary"
component="a"
sx={{
mr: 2,
paddingLeft: '1rem',
display: { xs: 'flex', md: 'flex' },
fontFamily: 'monospace',
fontWeight: 700,
letterSpacing: '0rem',
textDecoration: 'none',
}}
>
REQUEST.BOX
</Typography>
</Box>
{
user ? (
<>
<Box>
<NovuProvider subscriberId={'on-boarding-subscriber-id-123'} applicationIdentifier={'9SKjzgN_odAF'}>
<PopoverNotificationCenter colorScheme={'light'}>
{({ unseenCount }) => <NotificationBell unseenCount={unseenCount} />}
</PopoverNotificationCenter>
</NovuProvider>
</Box>
<Box>
<Chip
onClick={handleOpenUserMenu}
label={user.name}
color="secondary"
variant={'outlined'}
style={{ marginLeft: '10px', minWidth: '5rem', maxWidth:'10rem' }}
/>
<Menu
sx={{ mt: '45px' }}
id="menu-appbar"
anchorEl={anchorElUser}
anchorOrigin={{
vertical: 'top',
horizontal: 'right',
}}
keepMounted
transformOrigin={{
vertical: 'top',
horizontal: 'right',
}}
open={Boolean(anchorElUser)}
onClose={handleCloseUserMenu}
>
<MenuItem key="artistDashboard" onClick={handleCloseUserMenu}>
<Button fullWidth color="secondary" variant='contained' href="/artistDashboard">Artist Dashboard</Button>
</MenuItem>
<MenuItem key="myOrders" onClick={handleCloseUserMenu}>
<Button fullWidth color="primary" href="profile">My Orders</Button>
</MenuItem>
<MenuItem key="settings" onClick={handleCloseUserMenu}>
<Button fullWidth color="primary" href="/settings">Settings</Button>
</MenuItem>
<MenuItem key="logout" onClick={handleCloseUserMenu}>
<Button fullWidth color="error" href="/api/auth/logout">Logout</Button>
</MenuItem>
</Menu>
</Box>
</>
) : (
<>
<Button key="login" href='/api/auth/login' onClick={handleCloseNavMenu} sx={{ my: 2, display: 'block', color:"white" }}>
Login
</Button>
<Button key="signup" href='/api/auth/login' onClick={handleCloseNavMenu} sx={{ my: 2, display: 'block', color:"white" }}>
Signup
</Button>
</>
)}
</Toolbar>
</Container>
</AppBar>
);
}
export default ResponsiveAppBar;

View File

@ -1,41 +0,0 @@
import Head from "next/head";
import Header from "../header";
type LayoutProps = {
user?: any;
loading?: boolean;
children: React.ReactNode;
};
const Layout = ({ user, loading = false, children }: LayoutProps) => {
return (
<>
<Head>
<title>comissions.app</title>
</Head>
<Header user={user} loading={loading} />
<main>
<div className="container">{children}</div>
</main>
<style jsx>{`
.container {
max-width: 82rem;
margin: 1.5rem auto;
}
`}</style>
<style jsx global>{`
body {
margin: 0;
color: #333;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif;
}
`}</style>
</>
);
};
export default Layout;

View File

@ -1,31 +0,0 @@
import { useEffect, useRef } from "react"
export default function SwipeableViews(
{ className = "", index, onChangeIndex, ...rootProps }:
{ index: number, onChangeIndex: (index: number) => void } & React.HTMLProps<HTMLDivElement>
) {
const containerRef = useRef<HTMLDivElement>(null)
const scrollTimeout = useRef<number>()
useEffect(() => {
containerRef.current?.children[index]?.scrollIntoView({ behavior: "smooth" })
}, [index])
return (
<div
{...rootProps}
ref={containerRef}
className={
"flex snap-x snap-mandatory items-stretch overflow-x-scroll " +
"*:w-full *:flex-shrink-0 *:snap-center " + className
}
onScroll={({ currentTarget }) => {
if (scrollTimeout.current) clearTimeout(scrollTimeout.current)
scrollTimeout.current = window.setTimeout(() => {
const pageWidth = currentTarget.scrollWidth / currentTarget.children.length
onChangeIndex(Math.round(currentTarget.scrollLeft / pageWidth))
}, 100)
}}
/>
)
}

View File

@ -1,19 +1,8 @@
import * as React from 'react';
import Box from '@mui/material/Box';
import Stepper from '@mui/material/Stepper';
import Step from '@mui/material/Step';
import StepLabel from '@mui/material/StepLabel';
import StepContent from '@mui/material/StepContent';
import Button from '@mui/material/Button';
import Paper from '@mui/material/Paper';
import Typography from '@mui/material/Typography';
import Grid from '@mui/material/Grid'
import TextField from '@mui/material/TextField';
import ArtistDashboardRequest from '../components/Old/artistDashboardRequest';
import ArtistPortfolio from '../components/Old/artistPortfolio';
import EditableArtistPortfolio from '../components/Old/editableArtistPortfolio';
import {Box,Stepper,Step,StepLabel,StepContent,Button,Paper,Typography,Grid,TextField} from "@mui/material"
import ArtistDashboardRequest from './artistRequest';
import EditableArtistPortfolio from './artist/editablePortfolio';
import { useEffect, useState } from "react";
import CurrencyTextField from '@lupus-ai/mui-currency-textfield';
import {Card, CardContent, CardHeader, Divider } from '@mui/material';

View File

@ -9,7 +9,7 @@ import { useEffect, useState } from "react";
export default function ArtistRequest({id,userid,username,message,date,reload}) {
export default function AdminArtistRequest({id,userid,username,message,date,reload}) {
useEffect(() => {

View File

@ -0,0 +1,41 @@
import * as React from 'react';
import ImageListItem from '@mui/material/ImageListItem';
import { useState } from "react";
import Dialog from '@mui/material/Dialog';
import DialogContent from '@mui/material/DialogContent';
const AssetImage = ({ assetId, requestId }) => {
const [loaded, setLoaded] = useState(false);
const [openDialog, setOpenDialog] = useState(false);
const url = "/api/artist/requests/"+requestId+"/assets/"+assetId
const handleImageLoaded = () => {
setLoaded(true);
};
return (
<>
<ImageListItem key={assetId} onClick={() => setOpenDialog(true)}>
<img
srcSet={url}
src={url}
alt={assetId}
loading="lazy"
style={{ filter: loaded ? 'blur(0)' : 'blur(10px)', backgroundColor: 'grey', cursor: 'pointer' }}
onLoad={handleImageLoaded}
/>
</ImageListItem>
{/* Dialog for displaying full-screen image */}
<Dialog open={openDialog} onClose={() => setOpenDialog(false)}>
<DialogContent>
<img
src={url}
alt={assetId}
style={{ width: '100%', height: 'auto' }}
/>
</DialogContent>
</Dialog>
</>
);
}
export default AssetImage;

View File

@ -0,0 +1,41 @@
import * as React from 'react';
import ImageListItem from '@mui/material/ImageListItem';
import { useState } from "react";
import Dialog from '@mui/material/Dialog';
import DialogContent from '@mui/material/DialogContent';
const ReferenceImage = ({ referenceId, requestId }) => {
const [loaded, setLoaded] = useState(false);
const [openDialog, setOpenDialog] = useState(false);
const url = "/api/artist/requests/"+requestId+"/references/"+referenceId
const handleImageLoaded = () => {
setLoaded(true);
};
return (
<>
<ImageListItem key={referenceId} onClick={() => setOpenDialog(true)}>
<img
srcSet={url}
src={url}
alt={referenceId}
loading="lazy"
style={{ filter: loaded ? 'blur(0)' : 'blur(10px)', backgroundColor: 'grey', cursor: 'pointer' }}
onLoad={handleImageLoaded}
/>
</ImageListItem>
{/* Dialog for displaying full-screen image */}
<Dialog open={openDialog} onClose={() => setOpenDialog(false)}>
<DialogContent>
<img
src={url}
alt={referenceId}
style={{ width: '100%', height: 'auto' }}
/>
</DialogContent>
</Dialog>
</>
);
}
export default ReferenceImage;

View File

@ -1,9 +1,9 @@
import * as React from 'react';
import { ImageList, Box, Button, CircularProgress, Slider } from '@mui/material';
import { useEffect, useState } from "react";
import EditableArtistPortfolioImage from './editableArtistPortfolioImage';
import FileOpenIcon from '@mui/icons-material/FileOpen';
import { Grid } from '@mui/material';
import { Grid, ImageList, Box, Tooltip, CircularProgress, Slider, IconButton } from '@mui/material';
import { FileUpload } from '@mui/icons-material';
import EditableArtistPortfolioImage from './editablePortfolioImage';
const EditableArtistPortfolio = ({ artistId }) => {
const [portfolioData, setPortfolioData] = useState([]);
const [columns, setColumns] = useState(2);
@ -51,7 +51,7 @@ const EditableArtistPortfolio = ({ artistId }) => {
(
<Grid container spacing={2} sm={12}>
<Grid item xs={12} sm={12} sx={{ textAlign: "center" }}>
<Grid item xs={12} sm={1} sx={{ textAlign: "center" }}>
<input
id="portfolioUploadInput"
style={{ display: 'none' }}
@ -60,19 +60,15 @@ const EditableArtistPortfolio = ({ artistId }) => {
onChange={handlePortfolioUploadImageChange}
/>
<label htmlFor="portfolioUploadInput">
<Button
fullWidth
variant='outlined'
component="span"
size="large"
sx={{width:"100%"}}
startIcon={<FileOpenIcon />}
>
{(portfolioData.length > 0 ? "Upload Another Portfolio Image" : "Upload Your First Portfolio Image")}
</Button>
<Tooltip arrow title="Upload Image To Portfolio">
<IconButton color="primary" component="span">
<FileUpload sx={{fontSize:"2rem"}}/>
</IconButton>
</Tooltip>
</label>
</Grid>
<Grid item xs={12} sm={12} sx={{ textAlign: "center" }}>
<Grid item xs={12} sm={11} sx={{ textAlign: "center" }}>
<Tooltip arrow title="Amount of columns">
<Slider
defaultValue={columns}
aria-labelledby="discrete-slider"
@ -82,6 +78,8 @@ const EditableArtistPortfolio = ({ artistId }) => {
marks
min={1}
max={5}/>
</Tooltip>
</Grid>
<Grid item xs={12} sm={12} sx={{maxHeight:"45rem",overflowY:"scroll"}}>
<ImageList variant='masonry' cols={columns} sx={{ width:"100%" }}>

View File

@ -0,0 +1,59 @@
import * as React from 'react';
import DeleteIcon from '@mui/icons-material/Delete';
import { Dialog, DialogContent, ImageList,ImageListItem, ImageListItemBar } from '@mui/material';
import { IconButton } from '@mui/material';
import { useEffect, useState } from "react";
const EditableArtistPortfolioImage = ({ artistId, itemId, reload }) => {
const [loaded, setLoaded] = useState(false);
const [deleting, setDeleting] = useState(false);
const [openDialog, setOpenDialog] = useState(false); // State for controlling the dialog
const handleImageLoaded = () => {
setLoaded(true);
};
const deleteButton = () => {
setDeleting(true);
fetch('/api/artist/portfolio/' + itemId + "/delete", {
method: 'DELETE'
}).then(response => {
reload().then(data => {
})
})
}
return (
<>
<ImageListItem key={itemId} onClick={() => setOpenDialog(true)}>
<img
srcSet={process.env.NEXT_PUBLIC_API_URL + `/api/Discovery/Artists/${artistId}/Portfolio/${itemId}`}
src={process.env.NEXT_PUBLIC_API_URL + `/api/Discovery/Artists/${artistId}/Portfolio/${itemId}`}
alt={itemId}
loading="lazy"
style={{ filter: loaded ? 'blur(0)' : 'blur(10px)', backgroundColor: 'grey', cursor: 'pointer' }}
onLoad={handleImageLoaded}
/>
<ImageListItemBar
actionIcon={
<IconButton onClick={deleteButton} color="error">
<DeleteIcon />
</IconButton>
}>
</ImageListItemBar>
</ImageListItem>
{/* Dialog for displaying full-screen image */}
<Dialog open={openDialog} onClose={() => setOpenDialog(false)}>
<DialogContent>
<img
src={process.env.NEXT_PUBLIC_API_URL + `/api/Discovery/Artists/${artistId}/Portfolio/${itemId}`}
alt={itemId}
style={{ width: '100%', height: 'auto' }}
/>
</DialogContent>
</Dialog>
</>
);
}
export default EditableArtistPortfolioImage;

View File

@ -1,8 +1,7 @@
import * as React from 'react';
import {ImageList, Box, Typography, CircularProgress} from '@mui/material';
import { useEffect, useState } from "react";
import ArtistPortfolioImage from './artistPortfolioImage';
import ArtistPortfolioImage from './portfolioImage';
const ArtistPortfolio = ({masonry,columns,artistId}) => {
const [portfolioData, setPortfolioData] = useState([]);
const [profileId, setArtistId] = useState(artistId)

View File

@ -0,0 +1,41 @@
import * as React from 'react';
import ImageListItem from '@mui/material/ImageListItem';
import { useState } from "react";
import Dialog from '@mui/material/Dialog';
import DialogContent from '@mui/material/DialogContent';
const ArtistPortfolioImage = ({ artistId, itemId }) => {
const [loaded, setLoaded] = useState(false);
const [openDialog, setOpenDialog] = useState(false);
const handleImageLoaded = () => {
setLoaded(true);
};
return (
<>
<ImageListItem key={itemId} onClick={() => setOpenDialog(true)}>
<img
srcSet={process.env.NEXT_PUBLIC_API_URL + `/api/Discovery/Artists/${artistId}/Portfolio/${itemId}`}
src={process.env.NEXT_PUBLIC_API_URL + `/api/Discovery/Artists/${artistId}/Portfolio/${itemId}`}
alt={itemId}
loading="lazy"
style={{ filter: loaded ? 'blur(0)' : 'blur(10px)', backgroundColor: 'grey', cursor: 'pointer' }}
onLoad={handleImageLoaded}
/>
</ImageListItem>
{/* Dialog for displaying full-screen image */}
<Dialog open={openDialog} onClose={() => setOpenDialog(false)}>
<DialogContent>
<img
src={process.env.NEXT_PUBLIC_API_URL + `/api/Discovery/Artists/${artistId}/Portfolio/${itemId}`}
alt={itemId}
style={{ width: '100%', height: 'auto' }}
/>
</DialogContent>
</Dialog>
</>
);
}
export default ArtistPortfolioImage;

View File

@ -0,0 +1,85 @@
import * as React from 'react';
import { DataGrid } from '@mui/x-data-grid';
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';
export default function Reviews({artistId}) {
const router = useRouter();
const columns = [
{ field: 'requestId', headerName: 'ID', flex: 0.1},
{ field: 'message', headerName: 'Review', flex: 0.5},
{ field: 'rating', headerName: 'Rating', flex: 0.2, renderCell: (params) => {
return <Rating value={params.row.rating} readOnly />;
}}
];
const [isLoading, setIsLoading] = React.useState(true);
const [reviewCount, setReviewCount] = React.useState(null);
const [reviewData, setReviewData] = React.useState({});
const [paginationModel, setPaginationModel] = React.useState({
page: 0,
pageSize: 15,
});
const getReviews = async () => {
setIsLoading(true);
const response = await fetch('/api/discovery/artist/'+artistId+'/reviews', {
method: 'GET',
headers: {
'Content-Type': 'application/json',
}
});
const data = await response.json();
// 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',
headers: {
'Content-Type': 'application/json',
}
});
const data = await response.json();
setReviewCount(data);
setRowCountState((prevRowCountState) =>
data !== undefined
? data
: prevRowCountState,
);
return data;
}
// Some API clients return undefined while loading
// Following lines are here to prevent `rowCountState` from being undefined during the loading
const [rowCountState, setRowCountState] = React.useState(0);
React.useEffect(() => {
getReviews();
getReviewsCount();
}, [reviewCount, setRowCountState,paginationModel]);
return (
<div style={{ height: '100%', width: '100%' }}>
<LocalizationProvider dateAdapter={AdapterDayjs}>
<DataGrid
getRowId={(row) => row.requestId}
rows={reviewData}
columns={columns}
rowCount={rowCountState}
loading={isLoading}
pageSizeOptions={[15]}
paginationModel={paginationModel}
paginationMode="server"
onPaginationModelChange={setPaginationModel}
/>
</LocalizationProvider>
</div>
);
}

View File

@ -5,7 +5,7 @@ import CircularProgress from '@mui/material/CircularProgress';
import Box from '@mui/material/Box';
const ArtistDashboardRequest = () => {
const ArtistOnboardRequest = () => {
const [sellerRequestData, setArtistRequestData] = useState(null);
const getData = async () => {
@ -50,4 +50,4 @@ const ArtistDashboardRequest = () => {
))
)
}
export default ArtistDashboardRequest
export default ArtistOnboardRequest

View File

@ -0,0 +1,41 @@
import * as React from 'react';
import ImageListItem from '@mui/material/ImageListItem';
import { useState } from "react";
import Dialog from '@mui/material/Dialog';
import DialogContent from '@mui/material/DialogContent';
const AssetImage = ({ assetId, requestId }) => {
const [loaded, setLoaded] = useState(false);
const [openDialog, setOpenDialog] = useState(false);
const url = "/api/requests/"+requestId+"/assets/"+assetId
const handleImageLoaded = () => {
setLoaded(true);
};
return (
<>
<ImageListItem key={assetId} onClick={() => setOpenDialog(true)}>
<img
srcSet={url}
src={url}
alt={assetId}
loading="lazy"
style={{ filter: loaded ? 'blur(0)' : 'blur(10px)', backgroundColor: 'grey', cursor: 'pointer' }}
onLoad={handleImageLoaded}
/>
</ImageListItem>
{/* Dialog for displaying full-screen image */}
<Dialog open={openDialog} onClose={() => setOpenDialog(false)}>
<DialogContent>
<img
src={url}
alt={assetId}
style={{ width: '100%', height: 'auto' }}
/>
</DialogContent>
</Dialog>
</>
);
}
export default AssetImage;

View File

@ -0,0 +1,41 @@
import * as React from 'react';
import ImageListItem from '@mui/material/ImageListItem';
import { useState } from "react";
import Dialog from '@mui/material/Dialog';
import DialogContent from '@mui/material/DialogContent';
const ReferenceImage = ({ referenceId, requestId }) => {
const [loaded, setLoaded] = useState(false);
const [openDialog, setOpenDialog] = useState(false);
const url = "/api/requests/"+requestId+"/references/"+referenceId
const handleImageLoaded = () => {
setLoaded(true);
};
return (
<>
<ImageListItem key={referenceId} onClick={() => setOpenDialog(true)}>
<img
srcSet={url}
src={url}
alt={referenceId}
loading="lazy"
style={{ filter: loaded ? 'blur(0)' : 'blur(10px)', backgroundColor: 'grey', cursor: 'pointer' }}
onLoad={handleImageLoaded}
/>
</ImageListItem>
{/* Dialog for displaying full-screen image */}
<Dialog open={openDialog} onClose={() => setOpenDialog(false)}>
<DialogContent>
<img
src={url}
alt={referenceId}
style={{ width: '100%', height: 'auto' }}
/>
</DialogContent>
</Dialog>
</>
);
}
export default ReferenceImage;

View File

@ -12,11 +12,15 @@ import {Check, Refresh } from '@mui/icons-material';
import PriceCheckIcon from '@mui/icons-material/PriceCheck';
import AssignmentTurnedInIcon from '@mui/icons-material/AssignmentTurnedIn';
import AssignmentLateIcon from '@mui/icons-material/AssignmentLate';
import { useEffect, useState } from "react";
import { useMediaQuery } from '@mui/material';
import dayjs from 'dayjs';
export default function ServerPaginationGrid() {
export default function CustomerOrders() {
const isSmallScreen = useMediaQuery(theme => theme.breakpoints.down('sm')); // Check if the screen size is small
const columns = [
{ field: 'id', headerName: 'ID', flex: 0.1},
{ field: 'status', headerName: 'Status', flex: 0.15,
@ -41,23 +45,15 @@ export default function ServerPaginationGrid() {
}
}
},
{ field: 'amount', headerName: 'Amount', flex: 0.1, renderCell: (params) => {
{ field: 'amount', headerName: '$', flex: 0.1, renderCell: (params) => {
return <CurrencyTextField size="small" fullWidth value={params.row.amount} currencySymbol="$" disabled />;
}},
{ field: 'requestDate', headerName: 'Request Date', flex:0.15,
renderCell: (params) =>{
let formattedTime = ""
const date = new Date(params.row.requestDate);
formattedTime = date.toLocaleTimeString('en-US', { month: 'long', day: '2-digit', year: 'numeric', hour: '2-digit', minute: '2-digit' }); // Example format
return <DateField
size='small'
disabled
defaultValue={dayjs(params.row.requestDate)}
format="LL"
/>
} }
];
if(!isSmallScreen){
columns.push(
{ field: 'requestDate', headerName: 'Request Date', flex:0.15, type: 'date' ,
valueGetter: (params) => { return new Date(params.row.requestDate); }} );
}
const [isLoading, setIsLoading] = React.useState(true);
const [requestCount, setRequestCount] = React.useState(null);
const [requestData, setRequestData] = React.useState({});

View File

@ -1,61 +0,0 @@
import * as React from 'react';
import Box from '@mui/material/Box';
import Button from '@mui/material/Button';
import Dialog, { DialogProps } from '@mui/material/Dialog';
import DialogActions from '@mui/material/DialogActions';
import DialogContent from '@mui/material/DialogContent';
import DialogContentText from '@mui/material/DialogContentText';
import DialogTitle from '@mui/material/DialogTitle';
import FormControl from '@mui/material/FormControl';
import FormControlLabel from '@mui/material/FormControlLabel';
import InputLabel from '@mui/material/InputLabel';
import MenuItem from '@mui/material/MenuItem';
import Select, { SelectChangeEvent } from '@mui/material/Select';
import Switch from '@mui/material/Switch';
export default function RequestDialog() {
const [open, setOpen] = React.useState(false);
const handleClickOpen = () => {
setOpen(true);
};
const handleClose = () => {
setOpen(false);
};
return (
<React.Fragment>
<Dialog
fullWidth={fullWidth}
maxWidth={maxWidth}
open={open}
onClose={handleClose}
>
<DialogTitle>Optional sizes</DialogTitle>
<DialogContent>
<DialogContentText>
You can set my maximum width and whether to adapt or not.
</DialogContentText>
<Box
noValidate
component="form"
sx={{
display: 'flex',
flexDirection: 'column',
m: 'auto',
width: 'fit-content',
}}
>
<FormControl sx={{ mt: 2, minWidth: 120 }}>
<InputLabel htmlFor="max-width">maxWidth</InputLabel>
</FormControl>
</Box>
</DialogContent>
<DialogActions>
<Button onClick={handleClose}>Close</Button>
</DialogActions>
</Dialog>
</React.Fragment>
);
}

0
components/test.tsx Normal file
View File

View File

@ -1,325 +0,0 @@
import * as React from 'react';
import Box from '@mui/material/Box';
import Stepper from '@mui/material/Stepper';
import Step from '@mui/material/Step';
import StepLabel from '@mui/material/StepLabel';
import StepContent from '@mui/material/StepContent';
import Button from '@mui/material/Button';
import Paper from '@mui/material/Paper';
import Typography from '@mui/material/Typography';
import Grid from '@mui/material/Grid'
import TextField from '@mui/material/TextField';
import ArtistDashboardRequest from '../components/OLd/artistDashboardRequest';
import ArtistPortfolio from '../components/OLd/artistPortfolio';
import EditableArtistPortfolio from '../components/OLd/editableArtistPortfolio';
import { useEffect, useState } from "react";
import CurrencyTextField from '@lupus-ai/mui-currency-textfield';
import {Card, CardContent, CardHeader, Divider } from '@mui/material';
const steps = [
{
label: 'Request Access As Artist',
description: `In order to start selling your art on our platform, you need to request access. Please include links to your social media and tag or DM us on the platform (@RequestDotBox). We may reach out for further verification and examples of your work.`,
},
{
label: 'Onboard On Stripe',
description:
'Our platform uses Stripe as a payment processor. You will be required to onboard with them with all of your payout information and business information.',
},
{
label: 'Setup Your Portfolio',
description: `This is where you can setup your initial portfolio. You can upload any image format file to your portfolio. It will be automatically displayed on your artist page. You can add and remove from this later.`,
},
{
label: 'Configure Your Artist Page',
description: `Every artist gets their own public facing page that they can send to anyone or post anywhere. You have full control over the colors, logos, and more of this page.`,
},
];
export default function onboarding() {
const [activeStep, setActiveStep] = React.useState(0);
const [sellerRequestData, setArtistRequestData] = React.useState(null);
const [profileData, setArtistData] = React.useState(null);
const [isStripeOnboarded, setIsStripeOnboarded] = React.useState(false);
const [onBoardUrl, setOnBoardUrl] = React.useState("");
const handleNext = () => {
setActiveStep((prevActiveStep) => prevActiveStep + 1);
};
const handleBack = () => {
setActiveStep((prevActiveStep) => prevActiveStep - 1);
};
const handleReset = () => {
setActiveStep(0);
};
const getData = async () => {
const onboardCheckRequest = await fetch('/api/artist/onboarded', { method: "GET" });
const onboardCheckResponse = await onboardCheckRequest.json();
setIsStripeOnboarded(onboardCheckResponse["onboarded"]);
const onboardUrlRequest = await fetch('/api/artist/onboardurl', { method: "GET" });
const onboardUrlResponse = await onboardUrlRequest.json();
setOnBoardUrl(onboardUrlResponse["onboardUrl"]);
const response = await fetch('/api/artist/request');
const sellerRequest = await response.json();
setArtistRequestData(sellerRequest);
const profileResponse = await fetch('/api/artist/profile');
const sellerProfile = await profileResponse.json();
setArtistData(sellerProfile);
setTimeout(getData, 5000); // Poll every 5 seconds (adjust as needed)
}
React.useEffect(() => {
getData();
}, []);
const requestButton = () => {
fetch('/api/artist/newRequest').then((response) => {
if (response.ok) {
fetch('/api/artist/request').then((requestResponse) => {
requestResponse.json().then((sellerRequest) => {
setArtistRequestData(sellerRequest);
});
});
}
});
}
let formattedTime = ""
if (sellerRequestData) {
const date = new Date(sellerRequestData["requestDate"]);
formattedTime = date.toLocaleTimeString('en-US', { month: 'long', day: '2-digit', year: 'numeric', hour: '2-digit', minute: '2-digit' }); // Example format
}
if(activeStep==4){
window.location.href="/dashboard"
}
return (
<Card sx={{ width:"100%", padding:"2%" }}>
<Stepper activeStep={activeStep} orientation="vertical">
{steps.map((step, index) => (
<Step key={step.label}>
<StepLabel
optional={
index === 3 ? (
<Typography variant="caption">Last step</Typography>
) : null
}
>
{step.label}
</StepLabel>
{(index==0) ? (
<StepContent>
<Grid container >
<Grid item xs={12} lg={6}>
<Typography>{step.description}</Typography>
</Grid>
<Grid item xs={12} lg={6}>
</Grid>
{(sellerRequestData && Object.keys(sellerRequestData).length>0) ? (
<Grid item xs={12} lg={6} sx={{paddingTop:"2%"}}>
<ArtistDashboardRequest/>
</Grid>
):(
<Grid item xs={12} lg={6}>
<TextField fullWidth rows={4} multiline label="Application Message" variant="outlined" />
</Grid>
)}
</Grid>
<Box sx={{ mb: 2 ,paddingTop:"2%"}}>
<div>
{(sellerRequestData && Object.keys(sellerRequestData).length>0) ? (
(sellerRequestData["accepted"]) ? (
<Button variant="contained" onClick={handleNext}>
Continue
</Button>
) : (
<Button variant="contained" disabled>
Request Pending
</Button> )
):
(
<Button
variant="contained"
onClick={requestButton}
sx={{ mt: 1, mr: 1 }}
>
Request Access
</Button>
)}
</div>
</Box>
</StepContent>
): null}
{(index==1) ? (
<StepContent>
<Grid container>
<Grid item xs={12} lg={6}>
<Typography>{step.description}</Typography>
</Grid>
</Grid>
<Box sx={{ mb: 2 }}>
<div>
{isStripeOnboarded==true ? (
<Button
variant="contained"
onClick={handleNext}
sx={{ mt: 1, mr: 1 }}
>
Continue
</Button>
):(
<Button
color='success'
variant="contained"
href={onBoardUrl}
sx={{ mt: 1, mr: 1 }}
>
ONBOARD WITH STRIPE
</Button>
)}
</div>
</Box>
</StepContent>
): null}
{(index==2) ? (
<StepContent>
<Grid container>
<Grid item xs={12} lg={6}>
<Typography>{step.description}</Typography>
</Grid>
<Grid item xs={12} lg={6}>
<EditableArtistPortfolio artistId={profileData ? profileData["id"] : null}/>
</Grid>
</Grid>
<Box sx={{ mb: 2 }}>
<div>
<Button
variant="contained"
onClick={handleNext}
sx={{ mt: 1, mr: 1 }}
>
{index === steps.length - 1 ? 'Finish' : 'Continue'}
</Button>
</div>
</Box>
</StepContent>
): null}
{(index==3) ? (
<StepContent>
<Grid container>
<Grid item xs={12} lg={6}>
<Typography>{step.description}</Typography>
</Grid>
<Grid item xs={12} lg={6}>
</Grid>
<Grid container sx={{paddingTop:"50px"}}>
<Grid item xs={12} lg={2}>
</Grid>
<Grid item xs={12} lg={8}>
<Card elevation={5}>
<CardContent>
<Grid container>
<Grid item xs={12} lg={12} sx={{textAlign:"center"}}>
<Typography variant="h5" gutterBottom>
Artist Name
</Typography>
</Grid>
<Grid item xs={12} lg={12} sx={{textAlign:"center", paddingTop:"2%"}}>
<Typography variant="h6" gutterBottom>
Biography
</Typography>
</Grid>
<Grid item xs={12} lg={12} sx={{textAlign:"center"}}>
<Typography variant="body1" gutterBottom>
TAwehtwaehrewaoioirewaoihroiewahroiewahriewaohroiewahroiweahroiewahrhweaoirhewaiorhewaoirhewaoirhewaoirhweah
</Typography>
</Grid>
<Grid item xs={12} lg={12} sx={{textAlign:"center", paddingTop:"2%"}}>
<Typography variant="h6" gutterBottom>
Portfolio
</Typography>
</Grid>
<Grid item xs={12} lg={12} sx={{textAlign:"center"}}>
<ArtistPortfolio artistId={profileData ? profileData["id"] : null}/>
</Grid> <Divider/>
<Divider/>
<Grid item xs={12} lg={12} sx={{textAlign:"center", paddingTop:"2%"}}>
<Typography variant="h6" gutterBottom>
Requests
</Typography>
</Grid>
<Grid container sx={{textAlign:"center"}}>
<Grid item xs={12} lg={6} sx={{textAlign:"center", paddingTop:"2%"}}>
<TextField fullWidth disabled multiline rows={8} sx={{padding:"2%"}} label="Request Guidelines" value="These are the terms of your requests.">
</TextField>
</Grid>
<Grid item xs={12} lg={6} sx={{textAlign:"center", paddingTop:"2%"}}>
<TextField fullWidth multiline rows={4} sx={{padding:"2%"}} label="Your Request Terms">
</TextField>
<CurrencyTextField
label="Offer Amount"
variant="standard"
value={0}
currencySymbol="USD "
//minimumValue="0"
outputFormat="string"
decimalCharacter="."
digitGroupSeparator=","/>
<Button color="primary" variant="contained" sx={{paddingTop:"2%"}}>Submit</Button>
</Grid>
</Grid>
<Divider/>
</Grid>
</CardContent>
</Card>
</Grid>
<Grid item xs={12} lg={2}>
</Grid>
</Grid>
</Grid>
<Box sx={{ mb: 2 }}>
<div>
<Button
variant="contained"
onClick={handleNext}
sx={{ mt: 1, mr: 1 }}
>
{index === steps.length - 1 ? 'Finish' : 'Continue'}
</Button>
</div>
</Box>
</StepContent>
): null}
</Step>
))}
</Stepper>
{activeStep === steps.length && (
<Paper square elevation={0} sx={{ p: 3 }}>
<Typography>All steps completed - you&apos;re finished</Typography>
<Button onClick={handleReset} sx={{ mt: 1, mr: 1 }}>
Reset
</Button>
</Paper>
)}
</Card>
);
}

View File

@ -18,7 +18,7 @@ type ThemeConfig = {
const themeConfig: ThemeConfig = {
// ** Layout Configs
templateName: 'Request.Box' /* App Name */,
mode: 'light' /* light | dark */,
mode: 'dark' /* light | dark */,
contentWidth: 'boxed' /* full | boxed */,
// ** Routing Configs

View File

@ -7,7 +7,7 @@ import MuiAppBar, { AppBarProps } from '@mui/material/AppBar'
import MuiToolbar, { ToolbarProps } from '@mui/material/Toolbar'
// ** Type Import
import { Settings } from '../core/context/settingsContext'
import { Settings } from '../../../../context/settingsContext'
interface Props {
hidden: boolean

View File

@ -61,7 +61,6 @@ const VerticalNavHeader = (props: Props) => {
) : (
<Link href='/' passHref>
<StyledLink>
<img width={30} height={25} src= "https://s6.imgcdn.dev/ttLwl.png"/>
<HeaderTitle variant='h6' sx={{ ml: 3 }}>
{themeConfig.templateName}
</HeaderTitle>

View File

@ -65,7 +65,7 @@ const AppBarContent = (props: Props) => {
<Box className='actions-right' sx={{ display: 'flex', alignItems: 'center' }}>
{(profileData ? (
<NovuProvider subscriberId={profileData["id"]} applicationIdentifier={'9SKjzgN_odAF'}>
<PopoverNotificationCenter colorScheme={'light'}>
<PopoverNotificationCenter colorScheme={'dark'}>
{({ unseenCount }) => <NotificationBell unseenCount={unseenCount} />}
</PopoverNotificationCenter>
</NovuProvider>

View File

@ -4,20 +4,26 @@ import Table from 'mdi-material-ui/Table'
import CubeOutline from 'mdi-material-ui/CubeOutline'
import HomeOutline from 'mdi-material-ui/HomeOutline'
import SettingsApplicationsIcon from '@mui/icons-material/SettingsApplications';
import ListIcon from '@mui/icons-material/List';
// ** Type import
import { VerticalNavItemsType } from '../../core/layouts/types'
import { BankTransfer, Cart, Clipboard, PageFirst } from 'mdi-material-ui'
import { DocumentScanner, FileOpen, Settings } from '@mui/icons-material'
import { BankTransfer, Cart, Clipboard, PageFirst, StarOutline } from 'mdi-material-ui'
import { DocumentScanner, FileOpen, LockPerson, OpenInBrowser, People, PeopleOutline, Settings, WebAsset } from '@mui/icons-material'
import { useState, useEffect } from 'react'
const navigation = (): VerticalNavItemsType => {
const [isStripeOnboarded, setIsStripeOnboarded] = useState(false);
const [profileData, setProfileData] = useState(null);
const [userData, setUserData] = useState(null);
const [isAdmin, setIsAdmin] = useState(false);
const getData = async () => {
const adminCheck = await fetch('/api/admin/check', { method: "GET" });
if (adminCheck.status === 200) {
setIsAdmin(true);
}
const onboardCheckRequest = await fetch('/api/artist/onboarded', { method: "GET" });
const onboardCheckResponse = await onboardCheckRequest.json();
setIsStripeOnboarded(onboardCheckResponse["onboarded"]);
@ -37,22 +43,14 @@ const navigation = (): VerticalNavItemsType => {
}, []);
var result = [
{
sectionTitle: 'Admin'
},
{
title: 'Artist Requests',
icon: Clipboard,
path: '/dashboard/admin/requests'
},
{
sectionTitle: 'General'
},
{
title: 'Dashboard',
icon: HomeOutline,
path: '/dashboard'
},
{
sectionTitle: 'General'
},
{
title: 'Account Settings',
icon: Settings,
@ -60,39 +58,67 @@ const navigation = (): VerticalNavItemsType => {
},
{
title: 'Your Requests',
icon: Cart,
icon: ListIcon,
path: '/dashboard/requests'
}
];
if (isAdmin) {
result.push(
{
sectionTitle: 'Admin'
},
{
title: 'Manage Artist Access',
icon: LockPerson,
path: '/dashboard/admin/requests'
},
{
title: 'Manage Users',
icon: People,
path: '/dashboard/admin/users'
},
{
title: 'Manage Artists',
icon: PeopleOutline,
path: '/dashboard/admin/artists'
},
);
}
if (isStripeOnboarded) {
result.push(
{
sectionTitle: 'Artist'
},
{
title: 'Request Reviews',
icon: StarOutline,
path: '/dashboard/artist/reviews'
},
{
title: 'Incoming Requests',
icon: CubeOutline,
icon: ListIcon,
path: '/dashboard/artist/requests'
},
{
title: 'Payments/Payouts',
icon: BankTransfer,
path: '/dashboard/payout'
path: '/dashboard/artist/payout'
},
{
title: 'Shop Settings',
icon: SettingsApplicationsIcon,
title: 'Artist Settings',
icon: Settings,
path: '/dashboard/artist/artistsettings'
},
{
title: 'Page Settings',
icon: DocumentScanner,
icon: WebAsset,
path: '/dashboard/artist/pagesettings'
},
{
title: 'Your Page',
icon: FileOpen,
icon: OpenInBrowser,
path: '/box/' + (userData ? userData["displayName"] : "")
}
);

100
package-lock.json generated
View File

@ -17,13 +17,17 @@
"@mui/x-data-grid": "^6.19.4",
"@mui/x-date-pickers": "^6.19.4",
"@novu/notification-center": "^0.22.0",
"@types/formidable": "^3.4.5",
"apexcharts": "^3.45.2",
"axios": "^1.6.7",
"busboy": "^1.6.0",
"dayjs": "^1.11.10",
"formidable": "^3.5.1",
"jwt-decode": "^4.0.0",
"mdi-material-ui": "^7.8.0",
"mui-color-input": "^2.0.2",
"next": "latest",
"node-fetch": "^3.3.2",
"nprogress": "^0.2.0",
"openapi-typescript-fetch": "^1.1.3",
"react": "^18.2.0",
@ -1271,11 +1275,18 @@
}
}
},
"node_modules/@types/formidable": {
"version": "3.4.5",
"resolved": "https://registry.npmjs.org/@types/formidable/-/formidable-3.4.5.tgz",
"integrity": "sha512-s7YPsNVfnsng5L8sKnG/Gbb2tiwwJTY1conOkJzTMRvJAlLFW1nEua+ADsJQu8N1c0oTHx9+d5nqg10WuT9gHQ==",
"dependencies": {
"@types/node": "*"
}
},
"node_modules/@types/node": {
"version": "18.19.10",
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.10.tgz",
"integrity": "sha512-IZD8kAM02AW1HRDTPOlz3npFava678pr8Ie9Vp8uRhBROXAv8MXT2pCnGZZAKYdromsNQLHQcfWQ6EOatVLtqA==",
"dev": true,
"dependencies": {
"undici-types": "~5.26.4"
}
@ -1590,6 +1601,14 @@
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="
},
"node_modules/data-uri-to-buffer": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz",
"integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==",
"engines": {
"node": ">= 12"
}
},
"node_modules/date-fns": {
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/date-fns/-/date-fns-3.3.1.tgz",
@ -1728,6 +1747,28 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/fetch-blob": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz",
"integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/jimmywarting"
},
{
"type": "paypal",
"url": "https://paypal.me/jimmywarting"
}
],
"dependencies": {
"node-domexception": "^1.0.0",
"web-streams-polyfill": "^3.0.3"
},
"engines": {
"node": "^12.20 || >= 14.13"
}
},
"node_modules/find-root": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz",
@ -1765,6 +1806,17 @@
"node": ">= 6"
}
},
"node_modules/formdata-polyfill": {
"version": "4.0.10",
"resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz",
"integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==",
"dependencies": {
"fetch-blob": "^3.1.2"
},
"engines": {
"node": ">=12.20.0"
}
},
"node_modules/formidable": {
"version": "3.5.1",
"resolved": "https://registry.npmjs.org/formidable/-/formidable-3.5.1.tgz",
@ -2120,6 +2172,41 @@
}
}
},
"node_modules/node-domexception": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz",
"integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/jimmywarting"
},
{
"type": "github",
"url": "https://paypal.me/jimmywarting"
}
],
"engines": {
"node": ">=10.5.0"
}
},
"node_modules/node-fetch": {
"version": "3.3.2",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz",
"integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==",
"dependencies": {
"data-uri-to-buffer": "^4.0.0",
"fetch-blob": "^3.1.4",
"formdata-polyfill": "^4.0.10"
},
"engines": {
"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/node-fetch"
}
},
"node_modules/nprogress": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/nprogress/-/nprogress-0.2.0.tgz",
@ -2791,8 +2878,7 @@
"node_modules/undici-types": {
"version": "5.26.5",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
"integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==",
"dev": true
"integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="
},
"node_modules/url-join": {
"version": "4.0.1",
@ -2865,6 +2951,14 @@
"loose-envify": "^1.0.0"
}
},
"node_modules/web-streams-polyfill": {
"version": "3.3.3",
"resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz",
"integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==",
"engines": {
"node": ">= 8"
}
},
"node_modules/webfontloader": {
"version": "1.6.28",
"resolved": "https://registry.npmjs.org/webfontloader/-/webfontloader-1.6.28.tgz",

View File

@ -18,13 +18,17 @@
"@mui/x-data-grid": "^6.19.4",
"@mui/x-date-pickers": "^6.19.4",
"@novu/notification-center": "^0.22.0",
"@types/formidable": "^3.4.5",
"apexcharts": "^3.45.2",
"axios": "^1.6.7",
"busboy": "^1.6.0",
"dayjs": "^1.11.10",
"formidable": "^3.5.1",
"jwt-decode": "^4.0.0",
"mdi-material-ui": "^7.8.0",
"mui-color-input": "^2.0.2",
"next": "latest",
"node-fetch": "^3.3.2",
"nprogress": "^0.2.0",
"openapi-typescript-fetch": "^1.1.3",
"react": "^18.2.0",

View File

@ -1,103 +0,0 @@
// ** React Imports
import { SyntheticEvent, useState } from 'react'
// ** MUI Imports
import Box from '@mui/material/Box'
import Card from '@mui/material/Card'
import TabList from '@mui/lab/TabList'
import TabPanel from '@mui/lab/TabPanel'
import TabContext from '@mui/lab/TabContext'
import { styled } from '@mui/material/styles'
import MuiTab, { TabProps } from '@mui/material/Tab'
// ** Icons Imports
import AccountOutline from 'mdi-material-ui/AccountOutline'
import LockOpenOutline from 'mdi-material-ui/LockOpenOutline'
import InformationOutline from 'mdi-material-ui/InformationOutline'
// ** Demo Tabs Imports
import TabInfo from '../../views/account-settings/TabInfo'
import TabAccount from '../../views/account-settings/TabAccount'
import TabSecurity from '../../views/account-settings/TabSecurity'
// ** Third Party Styles Imports
import 'react-datepicker/dist/react-datepicker.css'
const Tab = styled(MuiTab)<TabProps>(({ theme }) => ({
[theme.breakpoints.down('md')]: {
minWidth: 100
},
[theme.breakpoints.down('sm')]: {
minWidth: 67
}
}))
const TabName = styled('span')(({ theme }) => ({
lineHeight: 1.71,
fontSize: '0.875rem',
marginLeft: theme.spacing(2.4),
[theme.breakpoints.down('md')]: {
display: 'none'
}
}))
const AccountSettings = () => {
// ** State
const [value, setValue] = useState<string>('account')
const handleChange = (event: SyntheticEvent, newValue: string) => {
setValue(newValue)
}
return (
<Card>
<TabContext value={value}>
<TabList
onChange={handleChange}
aria-label='account-settings tabs'
sx={{ borderBottom: theme => `1px solid ${theme.palette.divider}` }}
>
<Tab
value='account'
label={
<Box sx={{ display: 'flex', alignItems: 'center' }}>
<AccountOutline />
<TabName>Account</TabName>
</Box>
}
/>
<Tab
value='security'
label={
<Box sx={{ display: 'flex', alignItems: 'center' }}>
<LockOpenOutline />
<TabName>Security</TabName>
</Box>
}
/>
<Tab
value='info'
label={
<Box sx={{ display: 'flex', alignItems: 'center' }}>
<InformationOutline />
<TabName>Info</TabName>
</Box>
}
/>
</TabList>
<TabPanel sx={{ p: 0 }} value='account'>
<TabAccount />
</TabPanel>
<TabPanel sx={{ p: 0 }} value='security'>
<TabSecurity />
</TabPanel>
<TabPanel sx={{ p: 0 }} value='info'>
<TabInfo />
</TabPanel>
</TabContext>
</Card>
)
}
export default AccountSettings

View File

@ -1,37 +0,0 @@
import { useEffect, useState } from "react";
import { useUser } from "@auth0/nextjs-auth0/client";
import Layout from "../../components/Old/layout";
const ApiProfile = () => {
const { user, isLoading } = useUser();
const [data, setData] = useState(null);
useEffect(() => {
(async () => {
const res = await fetch("/api/protected-api");
const data = await res.json();
setData(data);
})();
}, []);
return (
<Layout user={user} loading={isLoading}>
<h1>Profile</h1>
<div>
<h3>Public page (client rendered)</h3>
<p>We are fetching data on the client-side :</p>
<p>By making request to '/api/protected-api' serverless function</p>
<p>so without a valid session cookie will fail</p>
<p>{JSON.stringify(data)}</p>
</div>
</Layout>
);
};
// Public route.(CSR) also accessing API from the client-side.
// data is not cached when redirecting between pages.
export default ApiProfile;

View File

@ -1,26 +0,0 @@
import { withPageAuthRequired } from "@auth0/nextjs-auth0";
import Layout from "../../components/Old/layout";
import { User } from "../../interfaces";
type ProfileProps = {
user: User;
};
export default function Profile({ user }: ProfileProps) {
return (
<Layout user={user}>
<h1>Profile</h1>
<div>
<h3>Profile (server rendered)</h3>
<img src={user.picture} alt="user picture" />
<p>nickname: {user.nickname}</p>
<p>name: {user.name}</p>
</div>
</Layout>
);
}
// Protected route, checking authentication status before rendering the page.(SSR)
// It's slower than a static page with client side authentication
export const getServerSideProps = withPageAuthRequired();

View File

@ -0,0 +1,18 @@
import { getAccessToken, withApiAuthRequired, getSession } from '@auth0/nextjs-auth0';
export default withApiAuthRequired(async function handler(req, res) {
const { accessToken } = await getAccessToken(req, res);
const { offset, pageSize } = req.body;
const response = await fetch(process.env.NEXT_PUBLIC_API_URL+'/api/admin/AdminArtists?offset='+offset+'&pageSize='+pageSize, {
headers: {
"Authorization": `Bearer ${accessToken}`
}
});
if(response.ok==false){
res.status(200).json({})
return;
}
let result = await response.json();
res.status(200).json(result);
});

View File

@ -0,0 +1,18 @@
import { getAccessToken, withApiAuthRequired, getSession } from '@auth0/nextjs-auth0';
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/'+artistId, {
headers: {
"Authorization": `Bearer ${accessToken}`
}
});
if(response.ok==false){
res.status(200).json({})
return;
}
let result = await response.json();
res.status(200).json(result);
});

View File

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

View File

@ -0,0 +1,19 @@
import { getAccessToken, withApiAuthRequired, getSession } from '@auth0/nextjs-auth0';
export default withApiAuthRequired(async function handler(req, res) {
const { accessToken } = await getAccessToken(req, res);
const { userId } = req.query;
const response = await fetch(process.env.NEXT_PUBLIC_API_URL+'/api/admin/AdminArtists/'+userId+"/Terminate", {
headers: {
"Authorization": `Bearer ${accessToken}`
},
method: req.method
});
if(response.ok==false){
res.status(200).json({})
return;
}
let result = await response.json();
res.status(200).json(result);
});

View File

@ -0,0 +1,19 @@
import { getAccessToken, withApiAuthRequired, getSession } from '@auth0/nextjs-auth0';
export default withApiAuthRequired(async function handler(req, res) {
const { accessToken } = await getAccessToken(req, res);
const { userId } = req.query;
const response = await fetch(process.env.NEXT_PUBLIC_API_URL+'/api/admin/AdminArtists/'+userId+"/Unsuspend", {
headers: {
"Authorization": `Bearer ${accessToken}`
},
method: req.method
});
if(response.ok==false){
res.status(200).json({})
return;
}
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 handler(req, res) {
const { accessToken } = await getAccessToken(req, res);
const response = await fetch(process.env.NEXT_PUBLIC_API_URL+'/api/admin/AdminArtists/Count', {
headers: {
"Authorization": `Bearer ${accessToken}`
}
});
if(response.ok==false){
res.status(200).json({})
return;
}
let result = await response.json();
res.status(200).json(result);
});

12
pages/api/admin/check.tsx Normal file
View File

@ -0,0 +1,12 @@
import { getAccessToken, withApiAuthRequired, getSession } from '@auth0/nextjs-auth0';
export default withApiAuthRequired(async function handler(req, res) {
const { accessToken } = await getAccessToken(req, res);
const response = await fetch(process.env.NEXT_PUBLIC_API_URL+'/api/admin/AdminArtistRequests', {
headers: {
"Authorization": `Bearer ${accessToken}`
}
});
res.status(response.status).json({})
});

View File

@ -1,8 +1,9 @@
import { getAccessToken, withApiAuthRequired, getSession } from '@auth0/nextjs-auth0';
export default withApiAuthRequired(async function onboardUrl(req, res) {
export default withApiAuthRequired(async function handler(req, res) {
const { accessToken } = await getAccessToken(req, res);
const response = await fetch(process.env.NEXT_PUBLIC_API_URL+'/api/admin/AdminArtistRequests', {
const { offset, pageSize } = req.body;
const response = await fetch(process.env.NEXT_PUBLIC_API_URL+'/api/admin/AdminArtistRequests?offset='+offset+'&pageSize='+pageSize, {
headers: {
"Authorization": `Bearer ${accessToken}`
}

View File

@ -0,0 +1,17 @@
import { getAccessToken, withApiAuthRequired, getSession } from '@auth0/nextjs-auth0';
export default withApiAuthRequired(async function handler(req, res) {
const { accessToken } = await getAccessToken(req, res);
const { requestId } = req.query;
const response = await fetch(process.env.NEXT_PUBLIC_API_URL+'/api/admin/AdminArtistRequests/'+requestId, {
headers: {
"Authorization": `Bearer ${accessToken}`,
},
method: req.method
});
console.log(response)
let result = await response.json();
res.status(200).json(result);
});
// handles ACCEPT AND DENY

View File

@ -0,0 +1,16 @@
import { getAccessToken, withApiAuthRequired, getSession } from '@auth0/nextjs-auth0';
export default withApiAuthRequired(async function handler(req, res) {
const { accessToken } = await getAccessToken(req, res);
const response = await fetch(process.env.NEXT_PUBLIC_API_URL+'/api/admin/AdminArtistRequests/Count', {
headers: {
"Authorization": `Bearer ${accessToken}`
}
});
if(response.ok==false){
res.status(200).json({})
return;
}
let result = await response.json();
res.status(200).json(result);
});

18
pages/api/admin/users.tsx Normal file
View File

@ -0,0 +1,18 @@
import { getAccessToken, withApiAuthRequired, getSession } from '@auth0/nextjs-auth0';
export default withApiAuthRequired(async function handler(req, res) {
const { accessToken } = await getAccessToken(req, res);
const { offset, pageSize } = req.body;
const response = await fetch(process.env.NEXT_PUBLIC_API_URL+'/api/admin/AdminUsers?offset='+offset+'&pageSize='+pageSize, {
headers: {
"Authorization": `Bearer ${accessToken}`
}
});
if(response.ok==false){
res.status(200).json({})
return;
}
let result = await response.json();
res.status(200).json(result);
});

View File

@ -0,0 +1,18 @@
import { getAccessToken, withApiAuthRequired, getSession } from '@auth0/nextjs-auth0';
export default withApiAuthRequired(async function handler(req, res) {
const { accessToken } = await getAccessToken(req, res);
const { userId } = req.query;
const response = await fetch(process.env.NEXT_PUBLIC_API_URL+'/api/admin/AdminUsers/'+userId, {
headers: {
"Authorization": `Bearer ${accessToken}`
}
});
if(response.ok==false){
res.status(200).json({})
return;
}
let result = await response.json();
res.status(200).json(result);
});

View File

@ -0,0 +1,19 @@
import { getAccessToken, withApiAuthRequired, getSession } from '@auth0/nextjs-auth0';
export default withApiAuthRequired(async function handler(req, res) {
const { accessToken } = await getAccessToken(req, res);
const { userId } = req.query;
const response = await fetch(process.env.NEXT_PUBLIC_API_URL+'/api/admin/AdminUsers/'+userId+"/Ban", {
headers: {
"Authorization": `Bearer ${accessToken}`
},
method: req.method
});
if(response.ok==false){
res.status(200).json({})
return;
}
let result = await response.json();
res.status(200).json(result);
});

View File

@ -0,0 +1,19 @@
import { getAccessToken, withApiAuthRequired, getSession } from '@auth0/nextjs-auth0';
export default withApiAuthRequired(async function handler(req, res) {
const { accessToken } = await getAccessToken(req, res);
const { userId } = req.query;
const response = await fetch(process.env.NEXT_PUBLIC_API_URL+'/api/admin/AdminUsers/'+userId+"/Suspend", {
headers: {
"Authorization": `Bearer ${accessToken}`
},
method: req.method
});
if(response.ok==false){
res.status(200).json({})
return;
}
let result = await response.json();
res.status(200).json(result);
});

View File

@ -0,0 +1,19 @@
import { getAccessToken, withApiAuthRequired, getSession } from '@auth0/nextjs-auth0';
export default withApiAuthRequired(async function handler(req, res) {
const { accessToken } = await getAccessToken(req, res);
const { userId } = req.query;
const response = await fetch(process.env.NEXT_PUBLIC_API_URL+'/api/admin/AdminUsers/'+userId+"/Unban", {
headers: {
"Authorization": `Bearer ${accessToken}`
},
method: req.method
});
if(response.ok==false){
res.status(200).json({})
return;
}
let result = await response.json();
res.status(200).json(result);
});

View File

@ -0,0 +1,19 @@
import { getAccessToken, withApiAuthRequired, getSession } from '@auth0/nextjs-auth0';
export default withApiAuthRequired(async function handler(req, res) {
const { accessToken } = await getAccessToken(req, res);
const { userId } = req.query;
const response = await fetch(process.env.NEXT_PUBLIC_API_URL+'/api/admin/AdminUsers/'+userId+"/Unsuspend", {
headers: {
"Authorization": `Bearer ${accessToken}`
},
method: req.method
});
if(response.ok==false){
res.status(200).json({})
return;
}
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 handler(req, res) {
const { accessToken } = await getAccessToken(req, res);
const response = await fetch(process.env.NEXT_PUBLIC_API_URL+'/api/admin/AdminUsers/Count', {
headers: {
"Authorization": `Bearer ${accessToken}`
}
});
if(response.ok==false){
res.status(200).json({})
return;
}
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

@ -7,7 +7,9 @@ export default withApiAuthRequired(async function onboardUrl(req, res) {
"Authorization": `Bearer ${accessToken}`
}
});
if(response.ok==false){
res.status(200).json({})
}
let result = await response.json();
res.status(200).json(result);
});

View File

@ -1,6 +1,6 @@
import { getAccessToken, withApiAuthRequired, getSession } from '@auth0/nextjs-auth0';
export default withApiAuthRequired(async function products(req, res) {
export default withApiAuthRequired(async function handler(req, res) {
const { accessToken } = await getAccessToken(req, res);
const requestId = req.query.requestId;
const response = await fetch(process.env.NEXT_PUBLIC_API_URL+'/api/Requests/Artist/'+requestId+'/Accept', {

View File

@ -0,0 +1,22 @@
import { getAccessToken, withApiAuthRequired, getSession } from '@auth0/nextjs-auth0';
import { IncomingForm } from 'formidable';
import fs from 'fs/promises';
async function createBlobFromFile(path: string): Promise<Blob> {
const file = await fs.readFile(path);
return new Blob([file]);
}
export default withApiAuthRequired(async function handler(req, res) {
const { accessToken } = await getAccessToken(req, res);
const requestId = req.query.requestId;
const response = await fetch(process.env.NEXT_PUBLIC_API_URL+'/api/Requests/Artist/'+requestId+'/Assets', {
headers: {
"Authorization": `Bearer ${accessToken}`,
'Content-Type': 'application/json'
},
method: req.method
});
let result = await response.json();
res.status(200).json(result);
});

View File

@ -0,0 +1,24 @@
import { getAccessToken, withApiAuthRequired } from '@auth0/nextjs-auth0';
import axios from 'axios';
import fs from 'fs';
import path from 'path';
export default withApiAuthRequired(async function handler(req, res) {
const { accessToken } = await getAccessToken(req, res);
const requestId = req.query.requestId;
const assetId = req.query.assetId;
const response = await axios({
method: 'get',
url: `${process.env.NEXT_PUBLIC_API_URL}/api/Requests/Artist/${requestId}/Assets/${assetId}`,
responseType: 'stream',
headers: {
"Authorization": `Bearer ${accessToken}`,
}
})
res.setHeader('Content-Type', response.headers['content-type']);
// Pipe the response stream directly to the response of the Next.js API Route
response.data.pipe(res);
});

View File

@ -1,6 +1,6 @@
import { getAccessToken, withApiAuthRequired, getSession } from '@auth0/nextjs-auth0';
export default withApiAuthRequired(async function products(req, res) {
export default withApiAuthRequired(async function handler(req, res) {
const { accessToken } = await getAccessToken(req, res);
const requestId = req.query.requestId;
const response = await fetch(process.env.NEXT_PUBLIC_API_URL+'/api/Requests/Artist/'+requestId+'/Complete', {

View File

@ -1,6 +1,6 @@
import { getAccessToken, withApiAuthRequired, getSession } from '@auth0/nextjs-auth0';
export default withApiAuthRequired(async function products(req, res) {
export default withApiAuthRequired(async function handler(req, res) {
const { accessToken } = await getAccessToken(req, res);
const requestId = req.query.requestId;
const response = await fetch(process.env.NEXT_PUBLIC_API_URL+'/api/Requests/Artist/'+requestId+'/Deny', {

View File

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

View File

@ -0,0 +1,52 @@
import { getAccessToken, withApiAuthRequired } from '@auth0/nextjs-auth0';
import { IncomingForm } from 'formidable'
import fs from 'fs/promises';
export const config = {
api: {
bodyParser: false,
},
};
async function createBlobFromFile(path: string): Promise<Blob> {
const file = await fs.readFile(path);
return new Blob([file]);
}
export default withApiAuthRequired(async function handler(req, res) {
const { accessToken } = await getAccessToken(req, res);
const form = new IncomingForm();
const requestId = req.query.requestId;
form.parse(req, async (err, fields, files) => {
if (err) {
console.error('Error parsing form:', err);
res.status(500).json({ error: 'Error parsing form' });
return;
}
const file = files["newImage"]; // Assuming your file input field name is 'file'
try {
const response = await fetch(process.env.NEXT_PUBLIC_API_URL+'/api/Requests/Artist/'+requestId+'/Assets', {
method: 'POST',
headers: {
"Authorization": `Bearer ${accessToken}`,
"Content-Type": " application/octet-stream"
},
body: await createBlobFromFile(file[0].filepath) // Don't set Content-Type, FormData will handle it
});
(response)
if (!response.ok) {
const errorData = await response.json();
throw new Error(errorData.message || 'Failed to upload file');
}
const responseData = await response.json();
res.status(200).json(responseData);
} catch (error) {
console.error('Error uploading file:', error);
res.status(500).json({ error: 'Error uploading file' });
}
});
});

View File

@ -0,0 +1,22 @@
import { getAccessToken, withApiAuthRequired, getSession } from '@auth0/nextjs-auth0';
import { IncomingForm } from 'formidable';
import fs from 'fs/promises';
async function createBlobFromFile(path: string): Promise<Blob> {
const file = await fs.readFile(path);
return new Blob([file]);
}
export default withApiAuthRequired(async function products(req, res) {
const { accessToken } = await getAccessToken(req, res);
const requestId = req.query.requestId;
const response = await fetch(process.env.NEXT_PUBLIC_API_URL+'/api/Requests/Artist/'+requestId+'/References', {
headers: {
"Authorization": `Bearer ${accessToken}`,
'Content-Type': 'application/json'
},
method: req.method
});
let result = await response.json();
res.status(200).json(result);
});

View File

@ -0,0 +1,24 @@
import { getAccessToken, withApiAuthRequired } from '@auth0/nextjs-auth0';
import axios from 'axios';
import fs from 'fs';
import path from 'path';
export default withApiAuthRequired(async function handler(req, res) {
const { accessToken } = await getAccessToken(req, res);
const requestId = req.query.requestId;
const referenceId = req.query.referenceId;
const response = await axios({
method: 'get',
url: `${process.env.NEXT_PUBLIC_API_URL}/api/Requests/Artist/${requestId}/References/${referenceId}`,
responseType: 'stream',
headers: {
"Authorization": `Bearer ${accessToken}`,
}
})
res.setHeader('Content-Type', response.headers['content-type']);
// Pipe the response stream directly to the response of the Next.js API Route
response.data.pipe(res);
});

View File

@ -0,0 +1,19 @@
import { getAccessToken, withApiAuthRequired, getSession } from '@auth0/nextjs-auth0';
export default withApiAuthRequired(async function onboardUrl(req, res) {
const { accessToken } = await getAccessToken(req, res);
const { completed, declined, accepted, paid, offset, pageSize } = req.body;
const apiUrl = `${process.env.NEXT_PUBLIC_API_URL}/api/Artist/Reviews`;
const response = await fetch(apiUrl, {
headers: {
"Authorization": `Bearer ${accessToken}`
}
});
if(response.ok==false){
res.status(200).json({})
return;
}
let result = await response.json();
res.status(200).json(result);
});

View File

@ -0,0 +1,19 @@
import { getAccessToken, withApiAuthRequired, getSession } from '@auth0/nextjs-auth0';
export default withApiAuthRequired(async function onboardUrl(req, res) {
const { accessToken } = await getAccessToken(req, res);
const { completed, declined, accepted, paid, offset, pageSize } = req.body;
const apiUrl = `${process.env.NEXT_PUBLIC_API_URL}/api/Artist/Reviews/Count`;
const response = await fetch(apiUrl, {
headers: {
"Authorization": `Bearer ${accessToken}`
}
});
if(response.ok==false){
res.status(200).json({})
return;
}
let result = await response.json();
res.status(200).json(result);
});

View File

@ -1,6 +1,3 @@
import { handleAuth } from "@auth0/nextjs-auth0";
exports.onExecutePostLogin = async (event, api) => {
const namespace = 'http://schemas.microsoft.com/ws/2008/06/identity/claims/role';
//console.log(event.authorization)
}
export default handleAuth();

View File

@ -1,24 +1,34 @@
import { useRouter } from 'next/router'
import { getAccessToken } from '@auth0/nextjs-auth0';
import fetch from 'node-fetch'; // Import node-fetch for making HTTP requests
export default async function handler(req, res ): Promise<any> {
const { artistName } = req.query;
var url = process.env.NEXT_PUBLIC_API_URL+`/api/Requests/Request`;
export default async function handler(req, res) {
if (req.method === 'POST') {
const url = process.env.NEXT_PUBLIC_API_URL + `/api/Requests/Request`;
const { accessToken } = await getAccessToken(req, res);
try {
const response = await fetch(url, {
method: 'POST',
headers: {
"Authorization": `Bearer ${accessToken}`,
'Content-Type': 'application/json'
},
body: req.body
body: req.body, // Pipe the incoming request directly to the outgoing request
});
//console.log(response)
if (!response.ok) {
throw new Error('Failed to fetch seller');
const errorData = await response.json();
res.status(response.status).json(errorData);
return;
}
let result = await response.json();
const result = await response.json();
res.status(200).json(result);
} catch (error) {
console.error('Error occurred during fetch:', error);
res.status(500).json({ error: 'An error occurred during the request' });
}
} else {
res.status(405).json({ error: 'Method Not Allowed' });
}
}

View File

@ -0,0 +1,13 @@
export default async function handler(req, res ): Promise<any> {
const { sellerId } = req.query;
var url = process.env.NEXT_PUBLIC_API_URL+`/api/Discovery/Artists/${sellerId}/Reviews`;
(url)
const response = await fetch(url);
if (!response.ok) {
throw new Error('Failed to fetch seller portfolio');
}
var result = await response.json();
(result)
res.status(200).json(result);
}

View File

@ -0,0 +1,13 @@
export default async function handler(req, res ): Promise<any> {
const { sellerId } = req.query;
var url = process.env.NEXT_PUBLIC_API_URL+`/api/Discovery/Artists/${sellerId}/Reviews/Count`;
(url)
const response = await fetch(url);
if (!response.ok) {
throw new Error('Failed to fetch seller portfolio');
}
var result = await response.json();
(result)
res.status(200).json(result);
}

View File

@ -3,7 +3,7 @@ import { withApiAuthRequired } from "@auth0/nextjs-auth0";
import { getAccessToken } from '@auth0/nextjs-auth0';
export default withApiAuthRequired(async function me(req, res) {
export default withApiAuthRequired(async function handler(req, res) {
if(req.method !== 'GET') {
////console.log(req.body)
const { accessToken } = await getAccessToken(req, res);

View File

@ -1,6 +1,6 @@
import { getAccessToken, withApiAuthRequired, getSession } from '@auth0/nextjs-auth0';
export default withApiAuthRequired(async function onboardUrl(req, res) {
export default withApiAuthRequired(async function handler(req, res) {
const { accessToken } = await getAccessToken(req, res);
const { completed, declined, accepted, paid, offset, pageSize } = req.body;
const apiUrl = `${process.env.NEXT_PUBLIC_API_URL}/api/Requests/Customer/Count?completed=${completed}&declined=${declined}&accepted=${accepted}&paid=${paid}&offset=${offset}&pageSize=${pageSize}`;

View File

@ -1,6 +1,6 @@
import { getAccessToken, withApiAuthRequired, getSession } from '@auth0/nextjs-auth0';
export default withApiAuthRequired(async function onboardUrl(req, res) {
export default withApiAuthRequired(async function handler(req, res) {
const { accessToken } = await getAccessToken(req, res);
const { completed, declined, accepted, paid, offset, pageSize } = req.body;
const apiUrl = `${process.env.NEXT_PUBLIC_API_URL}/api/Requests/Customer?completed=${completed}&declined=${declined}&accepted=${accepted}&paid=${paid}&offset=${offset}&pageSize=${pageSize}`;

View File

@ -0,0 +1,22 @@
import { getAccessToken, withApiAuthRequired, getSession } from '@auth0/nextjs-auth0';
import { IncomingForm } from 'formidable';
import fs from 'fs/promises';
async function createBlobFromFile(path: string): Promise<Blob> {
const file = await fs.readFile(path);
return new Blob([file]);
}
export default withApiAuthRequired(async function handler(req, res) {
const { accessToken } = await getAccessToken(req, res);
const requestId = req.query.requestId;
const response = await fetch(process.env.NEXT_PUBLIC_API_URL+'/api/Requests/Customer/'+requestId+'/Assets', {
headers: {
"Authorization": `Bearer ${accessToken}`,
'Content-Type': 'application/json'
},
method: req.method
});
let result = await response.json();
res.status(200).json(result);
});

View File

@ -0,0 +1,24 @@
import { getAccessToken, withApiAuthRequired } from '@auth0/nextjs-auth0';
import axios from 'axios';
import fs from 'fs';
import path from 'path';
export default withApiAuthRequired(async function handler(req, res) {
const { accessToken } = await getAccessToken(req, res);
const requestId = req.query.requestId;
const assetId = req.query.assetId;
const response = await axios({
method: 'get',
url: `${process.env.NEXT_PUBLIC_API_URL}/api/Requests/Customer/${requestId}/Assets/${assetId}`,
responseType: 'stream',
headers: {
"Authorization": `Bearer ${accessToken}`,
}
})
res.setHeader('Content-Type', response.headers['content-type']);
// Pipe the response stream directly to the response of the Next.js API Route
response.data.pipe(res);
});

View File

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

View File

@ -0,0 +1,52 @@
import { getAccessToken, withApiAuthRequired } from '@auth0/nextjs-auth0';
import { IncomingForm } from 'formidable'
import fs from 'fs/promises';
export const config = {
api: {
bodyParser: false,
},
};
async function createBlobFromFile(path: string): Promise<Blob> {
const file = await fs.readFile(path);
return new Blob([file]);
}
export default withApiAuthRequired(async function handler(req, res) {
const { accessToken } = await getAccessToken(req, res);
const form = new IncomingForm();
const requestId = req.query.requestId;
form.parse(req, async (err, fields, files) => {
if (err) {
console.error('Error parsing form:', err);
res.status(500).json({ error: 'Error parsing form' });
return;
}
const file = files["newImage"]; // Assuming your file input field name is 'file'
try {
const response = await fetch(process.env.NEXT_PUBLIC_API_URL+'/api/Requests/Customer/'+requestId+'/References', {
method: 'POST',
headers: {
"Authorization": `Bearer ${accessToken}`,
"Content-Type": " application/octet-stream"
},
body: await createBlobFromFile(file[0].filepath) // Don't set Content-Type, FormData will handle it
});
(response)
if (!response.ok) {
const errorData = await response.json();
throw new Error(errorData.message || 'Failed to upload file');
}
const responseData = await response.json();
res.status(200).json(responseData);
} catch (error) {
console.error('Error uploading file:', error);
res.status(500).json({ error: 'Error uploading file' });
}
});
});

View File

@ -1,6 +1,6 @@
import { getAccessToken, withApiAuthRequired, getSession } from '@auth0/nextjs-auth0';
export default withApiAuthRequired(async function products(req, res) {
export default withApiAuthRequired(async function handler(req, res) {
const { accessToken } = await getAccessToken(req, res);
const requestId = req.query.requestId;
const response = await fetch(process.env.NEXT_PUBLIC_API_URL+'/api/Requests/Customer/'+requestId+'/Payment', {

View File

@ -1,15 +1,20 @@
import { getAccessToken, withApiAuthRequired, getSession } from '@auth0/nextjs-auth0';
import { IncomingForm } from 'formidable';
import fs from 'fs/promises';
export default withApiAuthRequired(async function products(req, res) {
async function createBlobFromFile(path: string): Promise<Blob> {
const file = await fs.readFile(path);
return new Blob([file]);
}
export default withApiAuthRequired(async function handler(req, res) {
const { accessToken } = await getAccessToken(req, res);
const requestId = req.query.requestId;
const response = await fetch(process.env.NEXT_PUBLIC_API_URL+'/api/Requests/Customer/'+requestId+'/Reference', {
const response = await fetch(process.env.NEXT_PUBLIC_API_URL+'/api/Requests/Customer/'+requestId+'/References', {
headers: {
"Authorization": `Bearer ${accessToken}`,
'Content-Type': 'application/json'
},
method: req.method,
body: req.body
method: req.method
});
let result = await response.json();
res.status(200).json(result);

View File

@ -0,0 +1,24 @@
import { getAccessToken, withApiAuthRequired } from '@auth0/nextjs-auth0';
import axios from 'axios';
import fs from 'fs';
import path from 'path';
export default withApiAuthRequired(async function handler(req, res) {
const { accessToken } = await getAccessToken(req, res);
const requestId = req.query.requestId;
const referenceId = req.query.referenceId;
const response = await axios({
method: 'get',
url: `${process.env.NEXT_PUBLIC_API_URL}/api/Requests/Customer/${requestId}/References/${referenceId}`,
responseType: 'stream',
headers: {
"Authorization": `Bearer ${accessToken}`,
}
})
res.setHeader('Content-Type', response.headers['content-type']);
// Pipe the response stream directly to the response of the Next.js API Route
response.data.pipe(res);
});

View File

@ -0,0 +1,17 @@
import { getAccessToken, withApiAuthRequired, getSession } from '@auth0/nextjs-auth0';
export default withApiAuthRequired(async function handler(req, res) {
const { accessToken } = await getAccessToken(req, res);
const requestId = req.query.requestId;
const response = await fetch(process.env.NEXT_PUBLIC_API_URL+'/api/Requests/Customer/'+requestId+'/Review', {
headers: {
"Authorization": `Bearer ${accessToken}`,
'Content-Type': 'application/json'
},
method: 'PUT',
body: JSON.stringify(req.body)
});
let result = await response.json();
res.status(200).json(result);
});

View File

@ -16,13 +16,14 @@ import DialogContentText from '@mui/material/DialogContentText';
import DialogActions from '@mui/material/DialogActions';
import CurrencyTextField from '@lupus-ai/mui-currency-textfield';
import TextField from '@mui/material/TextField';
import ArtistPortfolio from "../../components/Old/artistPortfolio";
import ArtistPortfolio from "../../components/dashboard/artist/portfolio";
import { RouterNetwork } from "mdi-material-ui";
import { useRouter } from "next/router";
import { profile } from "console";
import FileOpen from "@mui/icons-material/FileOpen";
import Reviews from "../../components/dashboard/artist/reviews";
const Profile = () => {
const ArtistBox = () => {
const [profileData, setArtistData] = useState(null);
const [description, setDescription] = useState("");
@ -57,25 +58,29 @@ const Profile = () => {
const handleClose = () => {
setOpen(false);
};
const submitRequest = async (payload) => {
const formData = new FormData();
formData.append("artistId", payload.artistId);
formData.append("message", payload.message);
formData.append("amount", payload.amount);
formData.append("file", payload.file); // Append the file to FormData
try {
const requestResponse = await fetch('/api/box/newRequest', {
method: 'POST',
body: formData // Pass FormData containing the file
body: JSON.stringify({
artistId: profileData["id"],
message: payload.get('Message'),
amount: payload.get('Amount'),
})
});
if (requestResponse.ok) {
router.push("/dashboard/requests")
const requestResponseData = await requestResponse.json();
router.push("/dashboard/requests/"+requestResponseData["id"]);
} else {
alert("Error submitting request")
const errorData = await requestResponse.json();
alert("Error submitting request: " + errorData.detail);
}
} catch (error) {
console.error('Error submitting request:', error);
alert("Error submitting request. Please try again later.");
}
};
const getData = async () => {
if(router.query.artistName!=null){
@ -92,31 +97,6 @@ const Profile = () => {
getData()
}, [router.query.artistName]);
const columns: GridColDef[] = [
{
field: 'message',
headerName: 'Review',
flex: 0.75
},
{
field: 'rating',
headerName: 'Rating',
flex: 0.25,
renderCell: (params: GridValueGetterParams) => (
<Rating name="read-only" value={params.value} readOnly />
),
},
];
const rows = [
{ id: 1, message: 'Great work!', rating: 5 },
{ id: 2, message: 'BAD work!', rating: 1 },
{ id: 3, message: 'Okay work!', rating: 4 },
{ id: 4, message: 'Meh work!', rating: 2 },
{ id: 5, message: 'Great work!', rating: 5 },
{ id: 6, message: 'Mid work!', rating: 3 },
{ id: 7, message: 'HORRIBLE work!', rating: 1 },
];
return (
<>
@ -128,15 +108,7 @@ const Profile = () => {
onSubmit: (event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault();
const formData = new FormData(event.currentTarget);
const email = formData.get("email");
const file = formData.get("file"); // Get the file object
const payload = {
artistId: profileData["id"],
message: requestMessage,
amount: requestPrice,
file: file // Include the file in the payload
};
submitRequest(payload);
submitRequest(formData);
handleClose();
},
}}
@ -150,8 +122,8 @@ const Profile = () => {
autoFocus
required
margin="dense"
id="name"
name="message"
id="Message"
name="Message"
label="Request Message"
type="message"
fullWidth
@ -162,9 +134,10 @@ const Profile = () => {
value={requestMessage}
/>
<CurrencyTextField
label="Price"
label="Amount"
variant="standard"
currencySymbol="$"
name="Amount"
outputFormat="number"
decimalCharacter="."
digitGroupSeparator=","
@ -172,29 +145,6 @@ const Profile = () => {
onChange={handleRequestPriceChange}
value={requestPrice}
/>
<input
id="file"
name="file"
style={{ display: 'none' }}
accept="image/*"
type="file"
multiple
onChange={handleFileChange}
/>
<label htmlFor="file">
<Button
fullWidth
variant='outlined'
component="span"
size="large"
sx={{width:"100%"}}
startIcon={<FileOpen />}
>
Select Your Reference Images
</Button>
</label>
</DialogContent>
<DialogActions>
<Button onClick={handleClose}>Cancel</Button>
@ -264,19 +214,9 @@ const Profile = () => {
<Typography variant="h5" align="center" gutterBottom>REVIEWS HEADER</Typography>
</Grid>
<Grid item xs={12} md={12} >
<DataGrid
rows={rows}
columns={columns}
autoHeight={true}
initialState={{
pagination: {
paginationModel: {
pageSize: 5,
},
},
}}
pageSizeOptions={[5]}
/>
{profileData!=null ? (
<Reviews artistId={profileData["id"]}/>
):null}
</Grid>
</Grid>
</CardContent>
@ -312,6 +252,6 @@ const Profile = () => {
};
// Protected route, checking user authentication client-side.(CSR)
export default withPageAuthRequired(Profile);
export default withPageAuthRequired(ArtistBox);

View File

@ -1,83 +0,0 @@
// ** MUI Imports
import Grid from '@mui/material/Grid'
import Typography from '@mui/material/Typography'
// ** Demo Components Imports
import CardUser from '../views/cards/CardUser'
import CardImgTop from '../views/cards/CardImgTop'
import CardMobile from '../views/cards/CardMobile'
import CardSupport from '../views/cards/CardSupport'
import CardTwitter from '../views/cards/CardTwitter'
import CardFacebook from '../views/cards/CardFacebook'
import CardLinkedIn from '../views/cards/CardLinkedIn'
import CardAppleWatch from '../views/cards/CardAppleWatch'
import CardMembership from '../views/cards/CardMembership'
import CardInfluencer from '../views/cards/CardInfluencer'
import CardNavigation from '../views/cards/CardNavigation'
import CardWithCollapse from '../views/cards/CardWithCollapse'
import CardVerticalRatings from '../views/cards/CardVerticalRatings'
import CardNavigationCenter from '../views/cards/CardNavigationCenter'
import CardHorizontalRatings from '../views/cards/CardHorizontalRatings'
const CardBasic = () => {
return (
<Grid container spacing={6}>
<Grid item xs={12} sx={{ paddingBottom: 4 }}>
<Typography variant='h5'>Basic Cards</Typography>
</Grid>
<Grid item xs={12} sm={6} md={4}>
<CardImgTop />
</Grid>
<Grid item xs={12} sm={6} md={4}>
<CardUser />
</Grid>
<Grid item xs={12} sm={6} md={4}>
<CardWithCollapse />
</Grid>
<Grid item xs={12} sm={6}>
<CardMobile />
</Grid>
<Grid item xs={12} sm={6}>
<CardHorizontalRatings />
</Grid>
<Grid item xs={12} sm={6} md={4}>
<CardAppleWatch />
</Grid>
<Grid item xs={12} md={8}>
<CardMembership />
</Grid>
<Grid item xs={12} sm={6} md={4}>
<CardInfluencer />
</Grid>
<Grid item xs={12} sm={6} md={4}>
<CardVerticalRatings />
</Grid>
<Grid item xs={12} sm={6} md={4}>
<CardSupport />
</Grid>
<Grid item xs={12} sx={{ pb: 4, pt: theme => `${theme.spacing(17.5)} !important` }}>
<Typography variant='h5'>Navigation Cards</Typography>
</Grid>
<Grid item xs={12} md={6}>
<CardNavigation />
</Grid>
<Grid item xs={12} md={6}>
<CardNavigationCenter />
</Grid>
<Grid item xs={12} sx={{ pb: 4, pt: theme => `${theme.spacing(17.5)} !important` }}>
<Typography variant='h5'>Solid Cards</Typography>
</Grid>
<Grid item xs={12} sm={6} md={4}>
<CardTwitter />
</Grid>
<Grid item xs={12} sm={6} md={4}>
<CardFacebook />
</Grid>
<Grid item xs={12} sm={6} md={4}>
<CardLinkedIn />
</Grid>
</Grid>
)
}
export default CardBasic

View File

@ -0,0 +1,151 @@
import * as React from 'react';
import { DataGrid } from '@mui/x-data-grid';
import { GridColDef } from '@mui/x-data-grid';
import TextField from '@mui/material/TextField';
import { Button, Stack, Typography } from '@mui/material';
import CurrencyTextField from '@lupus-ai/mui-currency-textfield';
import { DateField } from '@mui/x-date-pickers/DateField';
import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider';
import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs';
import Chip from '@mui/material/Chip';
import {Block, Check, Close, Download, OpenInFull, OpenInNew, Refresh, Star, Upload } from '@mui/icons-material';
import PriceCheckIcon from '@mui/icons-material/PriceCheck';
import AssignmentTurnedInIcon from '@mui/icons-material/AssignmentTurnedIn';
import AssignmentLateIcon from '@mui/icons-material/AssignmentLate';
import ShoppingCartCheckoutIcon from '@mui/icons-material/ShoppingCartCheckout';
import { IconButton } from '@mui/material';
import Tooltip from '@mui/material/Tooltip';
import { Card, CardContent } from '@mui/material';
import Rating from '@mui/material/Rating';
import dayjs from 'dayjs';
import { DownloadBox, Magnify, StarOutline } from 'mdi-material-ui';
import { Dialog, DialogTitle, DialogContent, DialogContentText, DialogActions, FormControl, InputLabel, Box } from '@mui/material';
import { Grid } from '@mui/material';
import { useRouter } from 'next/router';
import { request } from 'http';
import { useMediaQuery } from '@mui/material';
export default function AdminArtists() {
const isSmallScreen = useMediaQuery(theme => theme.breakpoints.down('sm')); // Check if the screen size is small
const router = useRouter();
let columns = [];
if(isSmallScreen){
columns = [
{ field: 'id', headerName: '', flex: 0.05, sortable: false, filterable: false},
];
}
else{
columns = [
{ field: 'id', headerName: '', flex: 0.05, sortable: false, filterable: false},
{ field: 'displayName', headerName: 'User Name', flex: 0.15, sortable: false, filterable: false, valueGetter: (params) => {
return params.row.user.displayName
}},
{ field: 'name', headerName: 'Artist Name', flex: 0.15, sortable: false, filterable: false, valueGetter: (params) => {
return params.row.name
}},
{ field: 'email', headerName: 'Email', flex: 0.15, sortable: false, filterable: false, valueGetter: (params) => {
return params.row.user.email
}},
{ field: 'numberOfRequests', headerName: '# of Requests', flex: 0.1, sortable: false, filterable: false},
{ field: 'averageRating', headerName: 'Average Rating', flex: 0.1, sortable: false, filterable: false},
{ field: 'amountMade', headerName: 'Amount Made', flex: 0.1, sortable: false, filterable: false},
{ field: 'feesCollected', headerName: 'Fees Collected', flex: 0.1, sortable: false, filterable: false},
{ field: 'status', headerName: 'Status', flex: 0.1, sortable: false, filterable: false, renderCell: (params) =>{
if(params.row.user.banned){
return <Chip icon={<Block />} label="Banned" variant="outlined" color="error" />
}
else if(params.row.suspended || params.row.user.suspended){
return <Chip icon={<Block />} label="Suspended" variant="outlined" color="error" />
}
else{
return <Chip icon={<Check />} label="Active" variant="outlined" color="success" />
}
}},
{ 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/artists/"+params.row.id)}><OpenInNew /></IconButton></Tooltip>
}}
];
}
const [isLoading, setIsLoading] = React.useState(true);
const [requestCount, setRequestCount] = React.useState(null);
const [requestData, setRequestData] = React.useState({});
const [paginationModel, setPaginationModel] = React.useState({
page: 0,
pageSize: 15,
});
const getRequests = async () => {
setIsLoading(true);
const response = await fetch('/api/admin/artists', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
completed: true, // Example query parameter
declined: true, // Example query parameter
accepted: true, // Example query parameter
paid: true, // Example query parameter
offset: paginationModel.page*paginationModel.pageSize, // Example query parameter
pageSize: paginationModel.pageSize
}),
});
const data = await response.json();
setRequestData(data);
setIsLoading(false);
}
const getRequestsCount = async () => {
const response = await fetch('/api/admin/artists/count', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
completed: true, // Example query parameter
declined: true, // Example query parameter
accepted: true, // Example query parameter
paid: true, // Example query parameter
offset: paginationModel.page*paginationModel.pageSize, // Example query parameter
pageSize: paginationModel.pageSize
})
});
const data = await response.json();
setRequestCount(data);
setRowCountState((prevRowCountState) =>
data !== undefined
? data
: prevRowCountState,
);
return data;
}
// Some API clients return undefined while loading
// Following lines are here to prevent `rowCountState` from being undefined during the loading
const [rowCountState, setRowCountState] = React.useState(0);
React.useEffect(() => {
getRequests();
getRequestsCount();
}, [requestCount, setRowCountState,paginationModel]);
return (
<div style={{ height: '100%', width: '100%' }}>
<LocalizationProvider dateAdapter={AdapterDayjs}>
<DataGrid
rows={requestData}
columns={columns}
rowCount={rowCountState}
loading={isLoading}
pageSizeOptions={[15]}
paginationModel={paginationModel}
paginationMode="server"
onPaginationModelChange={setPaginationModel}
/>
</LocalizationProvider>
</div>
);
}

View File

@ -0,0 +1,104 @@
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);
}
}
useEffect(() => {
getData()
}, [router.query.artistId]);
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(AdminArtist);

View File

@ -1,50 +1,206 @@
import { useUser,withPageAuthRequired } from "@auth0/nextjs-auth0/client";
import { Grid, Typography } from "@mui/material";
import Card from "@mui/material/Card";
import CardContent from "@mui/material/CardContent";
import EditableArtistPortfolio from "../../../components/Old/editableArtistPortfolio";
import { useEffect, useState } from "react";
import * as React from 'react';
import { DataGrid } from '@mui/x-data-grid';
import { GridColDef } from '@mui/x-data-grid';
import TextField from '@mui/material/TextField';
import Button from '@mui/material/Button';
import Switch from '@mui/material/Switch';
import Divider from '@mui/material/Divider';
import ArtistRequest from "../../../components/ArtistRequest";
import { Button, Stack, Typography } from '@mui/material';
import CurrencyTextField from '@lupus-ai/mui-currency-textfield';
import { DateField } from '@mui/x-date-pickers/DateField';
import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider';
import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs';
import Chip from '@mui/material/Chip';
import {Block, Check, Close, Download, OpenInFull, OpenInNew, Person, Refresh, Star, Upload } from '@mui/icons-material';
import PriceCheckIcon from '@mui/icons-material/PriceCheck';
import AssignmentTurnedInIcon from '@mui/icons-material/AssignmentTurnedIn';
import AssignmentLateIcon from '@mui/icons-material/AssignmentLate';
import ShoppingCartCheckoutIcon from '@mui/icons-material/ShoppingCartCheckout';
import { IconButton } from '@mui/material';
import Tooltip from '@mui/material/Tooltip';
import { Card, CardContent } from '@mui/material';
import Rating from '@mui/material/Rating';
const ArtistRequests = () => {
const {user, isLoading} = useUser();
const [artistRequestData, setArtistRequestData] = useState(null);
const getData = () => {
fetch('/api/admin/requests').then(response => response.json().then(data => setArtistRequestData(data)))
}
useEffect(() => {
getData()
}, []);
return (
<Grid container spacing={2}>
{(artistRequestData != null && Object.keys(artistRequestData).length>0) ? (
(artistRequestData.map((request) => {
let formattedTime = "";
if (artistRequestData) {
const date = new Date(request["requestDate"]);
formattedTime = date.toLocaleTimeString('en-US', {
month: 'long',
day: '2-digit',
year: 'numeric',
hour12: true,
hour: '2-digit',
minute: '2-digit'
}); // Example format
import dayjs from 'dayjs';
import { DownloadBox, Magnify, StarOutline } from 'mdi-material-ui';
import { Dialog, DialogTitle, DialogContent, DialogContentText, DialogActions, FormControl, InputLabel, Box } from '@mui/material';
import { Grid } from '@mui/material';
import { useRouter } from 'next/router';
import { request } from 'http';
import { useMediaQuery } from '@mui/material';
export default function AdminRequests() {
const isSmallScreen = useMediaQuery(theme => theme.breakpoints.down('sm')); // Check if the screen size is small
const router = useRouter();
let columns = [];
if(isSmallScreen){
columns = [
{ field: 'id', headerName: '', flex: 0.05, sortable: false, filterable: false},
{ field: 'message', headerName: 'Message', flex: 0.2, sortable: false, filterable: false},
{ field: 'actions', headerName: '', flex: 0.15, sortable: false, filterable: false, renderCell: (params) => {
const handleAccept = async () => {
var response = await fetch("/api/admin/requests/"+params.row.userId, {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/"+params.row.userId, {method:"DELETE"})
if(response.ok){
var data = await response.json();
router.reload();
}
else{
alert("Failed to deny request.")
}
}
if(params.row.accepted){
return <>
<Tooltip title="Revoke artist access"><IconButton color="error"><Block/></IconButton></Tooltip>
<Tooltip title="View artist information"><IconButton onClick={() => router.push("/dashboard/admin/requests/"+params.row.id)} color="info"><Magnify/></IconButton></Tooltip>
</>
}
else{
return (
<ArtistRequest userid={request["userId"]} id={request["id"]} username={""} message={request["message"]} date={formattedTime} reload={getData} />
<>
<Tooltip title="Accept this request and grant artist access."><IconButton onClick={handleAccept} color="success"><Check/></IconButton></Tooltip>
<Tooltip title="Decline this request and refuse artist access."><IconButton onClick={handleDeny} color="error"><Close/></IconButton></Tooltip>
</>
)
}
))
):(<Typography>No requests</Typography>)}
</Grid>
);
};
}}
];
}
else{
columns = [
{ field: 'id', headerName: '', flex: 0.05, sortable: false, filterable: false},
{ field: 'userId', headerName: 'User ID', flex: 0.25, sortable: false, filterable: false},
{ field: 'message', headerName: 'Message', flex: 0.5, sortable: false, filterable: false},
{ field: 'requestDate', headerName: 'Request Date', flex: 0.1, sortable: false, filterable: false, type: 'date', valueGetter: (params) => { return new Date(params.row.requestDate); }},
{ field: 'accepted', headerName:'Accepted', flex: 0.15, sortable: false, filterable: false, renderCell: (params) => {
return (params.row.accepted ? <Chip icon={<Check />} label="Accepted" variant="outlined" color="success" /> : <Chip icon={<Refresh />} label="Pending" variant="outlined" color="info" />)
}},
{ field: 'actions', headerName: '', flex: 0.15, sortable: false, filterable: false, renderCell: (params) => {
const handleAccept = async () => {
var response = await fetch("/api/admin/requests/"+params.row.userId, {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/"+params.row.userId, {method:"DELETE"})
if(response.ok){
var data = await response.json();
router.reload();
}
else{
alert("Failed to deny request.")
}
}
if(params.row.accepted){
return <>
<Tooltip title="View request information"><IconButton onClick={() => router.push("/dashboard/admin/requests/"+params.row.id)} color="info"><OpenInNew/></IconButton></Tooltip>
<Tooltip title="View artist information"><IconButton onClick={() => router.push("/dashboard/admin/users/"+params.row.id)} color="info"><Person/></IconButton></Tooltip>
</>
}
else{
// Protected route, checking user authentication client-side.(CSR)
export default withPageAuthRequired(ArtistRequests);
return (
<>
<Tooltip title="View request information"><IconButton onClick={() => router.push("/dashboard/admin/requests/"+params.row.id)} color="info"><OpenInNew/></IconButton></Tooltip>
<Tooltip title="Accept this request and grant artist access."><IconButton onClick={handleAccept} color="success"><Check/></IconButton></Tooltip>
<Tooltip title="Decline this request and refuse artist access."><IconButton onClick={handleDeny} color="error"><Close/></IconButton></Tooltip>
</>
)
}
}}
];
}
const [isLoading, setIsLoading] = React.useState(true);
const [requestCount, setRequestCount] = React.useState(null);
const [requestData, setRequestData] = React.useState({});
const [paginationModel, setPaginationModel] = React.useState({
page: 0,
pageSize: 15,
});
const getRequests = async () => {
setIsLoading(true);
const response = await fetch('/api/admin/requests', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
completed: true, // Example query parameter
declined: true, // Example query parameter
accepted: true, // Example query parameter
paid: true, // Example query parameter
offset: paginationModel.page*paginationModel.pageSize, // Example query parameter
pageSize: paginationModel.pageSize
}),
});
const data = await response.json();
setRequestData(data);
setIsLoading(false);
}
const getRequestsCount = async () => {
const response = await fetch('/api/admin/requests/count', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
completed: true, // Example query parameter
declined: true, // Example query parameter
accepted: true, // Example query parameter
paid: true, // Example query parameter
offset: paginationModel.page*paginationModel.pageSize, // Example query parameter
pageSize: paginationModel.pageSize
})
});
const data = await response.json();
setRequestCount(data);
setRowCountState((prevRowCountState) =>
data !== undefined
? data
: prevRowCountState,
);
return data;
}
// Some API clients return undefined while loading
// Following lines are here to prevent `rowCountState` from being undefined during the loading
const [rowCountState, setRowCountState] = React.useState(0);
React.useEffect(() => {
getRequests();
getRequestsCount();
}, [requestCount, setRowCountState,paginationModel]);
return (
<div style={{ height: '100%', width: '100%' }}>
<LocalizationProvider dateAdapter={AdapterDayjs}>
<DataGrid
rows={requestData}
columns={columns}
rowCount={rowCountState}
loading={isLoading}
pageSizeOptions={[15]}
paginationModel={paginationModel}
paginationMode="server"
onPaginationModelChange={setPaginationModel}
/>
</LocalizationProvider>
</div>
);
}

View File

@ -0,0 +1,124 @@
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 [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.requestId]);
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>
{request?.accepted ? (
<Grid item xs={12} md={12}>
<Button fullWidth variant="contained" onClick={handleAccept} color="error">Revoke Artist Access</Button>
</Grid>
):(
<>
<Grid item xs={12} md={6}>
<Button fullWidth variant="contained" onClick={handleAccept} color="primary">Accept</Button>
</Grid>
<Grid item xs={12} md={6}>
<Button fullWidth variant="contained" onClick={handleDeny} color="secondary">Reject</Button>
</Grid>
</>
)}
</Grid>
</Grid>
<Grid item xs={6}>
</Grid>
</Grid>
</CardContent>
</Card>
</>
);
};
// Protected route, checking user authentication client-side.(CSR)
export default withPageAuthRequired(AdminRequest);

View File

@ -0,0 +1,147 @@
import * as React from 'react';
import { DataGrid } from '@mui/x-data-grid';
import { GridColDef } from '@mui/x-data-grid';
import TextField from '@mui/material/TextField';
import { Button, Stack, Typography } from '@mui/material';
import CurrencyTextField from '@lupus-ai/mui-currency-textfield';
import { DateField } from '@mui/x-date-pickers/DateField';
import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider';
import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs';
import Chip from '@mui/material/Chip';
import {Block, Check, Close, Download, OpenInFull, OpenInNew, Refresh, Star, Upload } from '@mui/icons-material';
import PriceCheckIcon from '@mui/icons-material/PriceCheck';
import AssignmentTurnedInIcon from '@mui/icons-material/AssignmentTurnedIn';
import AssignmentLateIcon from '@mui/icons-material/AssignmentLate';
import ShoppingCartCheckoutIcon from '@mui/icons-material/ShoppingCartCheckout';
import { IconButton } from '@mui/material';
import Tooltip from '@mui/material/Tooltip';
import { Card, CardContent } from '@mui/material';
import Rating from '@mui/material/Rating';
import dayjs from 'dayjs';
import { DownloadBox, Magnify, StarOutline } from 'mdi-material-ui';
import { Dialog, DialogTitle, DialogContent, DialogContentText, DialogActions, FormControl, InputLabel, Box } from '@mui/material';
import { Grid } from '@mui/material';
import { useRouter } from 'next/router';
import { request } from 'http';
import { useMediaQuery } from '@mui/material';
export default function AdminUsers() {
const isSmallScreen = useMediaQuery(theme => theme.breakpoints.down('sm')); // Check if the screen size is small
const router = useRouter();
let columns = [];
if(isSmallScreen){
columns = [
{ field: 'id', headerName: 'User ID', flex: 0.95, sortable: false, filterable: false},
{ field: "actions", headerName: "Actions", 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>
}}
];
}
else{
columns = [
{ 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: "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){
return <Chip icon={<Block />} label="Banned" variant="outlined" color="error" />
}
else if(params.row.suspended){
return <Chip icon={<Block />} label="Suspended" variant="outlined" color="error" />
}
else{
return <Chip icon={<Check />} label="Active" variant="outlined" color="success" />
}
}},
{ 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>
}}
];
}
const [isLoading, setIsLoading] = React.useState(true);
const [requestCount, setRequestCount] = React.useState(null);
const [requestData, setRequestData] = React.useState({});
const [paginationModel, setPaginationModel] = React.useState({
page: 0,
pageSize: 15,
});
const getRequests = async () => {
setIsLoading(true);
const response = await fetch('/api/admin/users', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
completed: true, // Example query parameter
declined: true, // Example query parameter
accepted: true, // Example query parameter
paid: true, // Example query parameter
offset: paginationModel.page*paginationModel.pageSize, // Example query parameter
pageSize: paginationModel.pageSize
}),
});
const data = await response.json();
setRequestData(data);
setIsLoading(false);
}
const getRequestsCount = async () => {
const response = await fetch('/api/admin/users/count', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
completed: true, // Example query parameter
declined: true, // Example query parameter
accepted: true, // Example query parameter
paid: true, // Example query parameter
offset: paginationModel.page*paginationModel.pageSize, // Example query parameter
pageSize: paginationModel.pageSize
})
});
const data = await response.json();
setRequestCount(data);
setRowCountState((prevRowCountState) =>
data !== undefined
? data
: prevRowCountState,
);
return data;
}
// Some API clients return undefined while loading
// Following lines are here to prevent `rowCountState` from being undefined during the loading
const [rowCountState, setRowCountState] = React.useState(0);
React.useEffect(() => {
getRequests();
getRequestsCount();
}, [requestCount, setRowCountState,paginationModel]);
return (
<div style={{ height: '100%', width: '100%' }}>
<Button target="_blank" href="https://manage.auth0.com/dashboard">Open Auth0 Dashboard</Button>
<LocalizationProvider dateAdapter={AdapterDayjs}>
<DataGrid
rows={requestData}
columns={columns}
rowCount={rowCountState}
loading={isLoading}
pageSizeOptions={[15]}
paginationModel={paginationModel}
paginationMode="server"
onPaginationModelChange={setPaginationModel}
/>
</LocalizationProvider>
</div>
);
}

View File

@ -0,0 +1,104 @@
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 [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.userId]);
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>
</>
);
};
// Protected route, checking user authentication client-side.(CSR)
export default withPageAuthRequired(AdminUser);

View File

@ -1,15 +1,16 @@
import { useUser,withPageAuthRequired } from "@auth0/nextjs-auth0/client";
import { CircularProgress, Grid, Typography } from "@mui/material";
import { CircularProgress, Grid, IconButton, Tooltip, Typography } from "@mui/material";
import Card from "@mui/material/Card";
import CardContent from "@mui/material/CardContent";
import EditableArtistPortfolio from "../../../components/Old/editableArtistPortfolio";
import EditableArtistPortfolio from "../../../components/dashboard/artist/editablePortfolio";
import { useEffect, useState } from "react";
import TextField from '@mui/material/TextField';
import Button from '@mui/material/Button';
import Switch from '@mui/material/Switch';
import Divider from '@mui/material/Divider';
import ArtistRequest from "../../../components/ArtistRequest";
import ArtistRequest from "../../../components/dashboard/admin/artistRequest";
import Box from '@mui/material/Box';
import { Save } from "@mui/icons-material";
const ArtistSettings = () => {
const {user, isLoading} = useUser();
@ -91,8 +92,10 @@ const saveChanges = async () => {
<Grid item xs={12} md={8}>
<Typography variant="h5" gutterBottom>General Settings</Typography>
</Grid>
<Grid item xs={12} md={4} >
<Button variant="contained" onClick={saveChanges} color="success" fullWidth>{saved ? "Saved" : "Save Changes"}</Button>
<Grid item xs={12} md={4} sx={{textAlign:"right"}} >
<Tooltip arrow title="Save Changes">
<IconButton onClick={saveChanges} color="success"><Save /></IconButton>
</Tooltip>
</Grid>
<Grid item xs={12} md={12} >
<Grid container spacing={4} sx={{paddingTop:"2%"}}>

View File

@ -7,12 +7,13 @@ import CardMedia from '@mui/material/CardMedia';
import Button from '@mui/material/Button';
import Typography from '@mui/material/Typography';
import { useEffect, useState } from "react";
import EditableArtistPortfolio from "../../../components/Old/editableArtistPortfolio";
import ArtistPortfolio from "../../../components/Old/artistPortfolio";
import EditableArtistPortfolio from "../../../components/dashboard/artist/editablePortfolio";
import ArtistPortfolio from "../../../components/dashboard/artist/portfolio";
import { DataGrid, GridColDef, GridValueGetterParams } from '@mui/x-data-grid';
import Rating from '@mui/material/Rating';
import CircularProgress from '@mui/material/CircularProgress';
import Box from '@mui/material/Box';
import Reviews from "../../../components/dashboard/artist/reviews";
const Profile = () => {
@ -132,19 +133,9 @@ const Profile = () => {
<Typography variant="h5" align="center" gutterBottom>REVIEWS HEADER</Typography>
</Grid>
<Grid item xs={12} md={12} >
<DataGrid
rows={rows}
columns={columns}
autoHeight={true}
initialState={{
pagination: {
paginationModel: {
pageSize: 5,
},
},
}}
pageSizeOptions={[5]}
/>
{profileData!=null ? (
<Reviews artistId={profileData["id"]}/>
):null}
</Grid>
</Grid>
</CardContent>

View File

@ -1,5 +1,5 @@
import { useUser,withPageAuthRequired } from "@auth0/nextjs-auth0/client";
import { CardHeader, Grid, Typography } from "@mui/material";
import { CardHeader, Grid, IconButton, Typography } from "@mui/material";
import Card from "@mui/material/Card";
import CardContent from "@mui/material/CardContent";
import { useEffect, useState } from "react";
@ -7,6 +7,7 @@ import CurrencyTextField from '@lupus-ai/mui-currency-textfield';
import Button from '@mui/material/Button';
import { OpenInNew } from "@mui/icons-material";
import CircularProgress from '@mui/material/CircularProgress';
import Tooltip from '@mui/material/Tooltip';
import Box from '@mui/material/Box';
const Payout = () => {
@ -33,23 +34,16 @@ const Payout = () => {
<Grid container spacing={2}>
<Grid item xs={12} md={4}>
<Card>
<CardHeader title="Payout Settings" />
<CardHeader title="Payout Settings" action ={<Tooltip title="Open Stripe dashboard"><IconButton href={payoutData ? payoutData["payoutUrl"] : ""} target="_blank" color="info"><OpenInNew/></IconButton></Tooltip>}/>
<CardContent>
<Grid container spacing={2}>
<Grid item xs={12} md={4}>
<Grid container>
<Grid item xs={12} md={12}>
<Grid item xs={12} md={6}>
<CurrencyTextField fullWidth label="Current Balance" size="large" disabled variant="standard" currencySymbol="$" outputFormat="string" decimalCharacter="." digitGroupSeparator="," value={payoutData ? payoutData["balance"] : ""} />
</Grid>
<Grid item xs={12} md={12}>
<Grid item xs={12} md={6}>
<CurrencyTextField fullWidth label="Pending Balance" size="large" disabled variant="standard" currencySymbol="$" outputFormat="string" decimalCharacter="." digitGroupSeparator="," value={payoutData ? payoutData["pendingBalance"] : ""} />
</Grid>
</Grid>
</Grid>
<Grid item xs={12} md={8}>
<Button fullWidth href={payoutData ? payoutData["payoutUrl"] : ""} target="_blank" startIcon={<OpenInNew/>} variant="contained" color="info" size="large">Open Dashboard</Button>
</Grid>
</Grid>
</CardContent>
</Card>
</Grid>

View File

@ -21,63 +21,47 @@ import AssignmentTurnedInIcon from '@mui/icons-material/AssignmentTurnedIn';
import AssignmentLateIcon from '@mui/icons-material/AssignmentLate';
import { Card, CardHeader } from '@mui/material';
import { Dialog, DialogActions, DialogContent, DialogTitle, Grid, Stack } from '@mui/material';
import RequestReferenceImage from '../../../components/Old/requestReferencesImage';
import RequestReferences from '../../../components/Old/requestReferences';
import { useMediaQuery } from '@mui/material';
export default function ServerPaginationGrid() {
const isSmallScreen = useMediaQuery(theme => theme.breakpoints.down('sm')); // Check if the screen size is small
const router = useRouter();
const columns = [
{ field: 'id', headerName: 'ID', flex: 0.1},
{ field: 'status', headerName: 'Status', flex: 0.15,
let columns = []
if(isSmallScreen){
columns = [
{ field: 'id', headerName: 'ID', flex: 0.1, filterable: false, sortable: false},
{ field: 'status', headerName: 'Status', flex: 0.15, filterable: false, sortable: false,
renderCell: (params) => {
if(params.row.completed){
return <Chip icon={<Check />} label="Completed" variant="filled" color="success" />
return <Check color="success" />
}
else if(params.row.paid){
return <Chip icon={<PriceCheckIcon />} label="Paid" variant="filled" color="success" />
return <PriceCheckIcon color="success" />
}
else if(params.row.accepted && params.row.paid==false){
return <Chip icon={<PriceCheckIcon />} label="Pending Payment" variant="filled" color="warning" />
return <PriceCheckIcon color="warning" />
}
else if(params.row.accepted && params.row.paid){
return <Chip icon={<AssignmentTurnedInIcon />} label="Accepted" variant="filled" color="info" />
return <AssignmentTurnedInIcon color="info" />
}
else if(params.row.declined){
return <Chip icon={<AssignmentLateIcon />} label="Declined" variant="filled" color="error" />
return <AssignmentLateIcon color="error" />
}
else{
return <Chip icon={<Refresh />} label="Pending" variant="filled" color="secondary" />
return <Refresh color="secondary" />
}
}
},
{ field: 'message', headerName: 'Message', flex: 0.5,
renderCell: (params) => {
return <TextField size="small" fullWidth value={params.row.message} disabled />;
}},
{ field: 'amount', headerName: 'Amount', flex: 0.1, renderCell: (params) => {
{ field: 'amount', headerName: 'Amount', flex: 0.15, filterable: false, sortable: false, renderCell: (params) => {
return <CurrencyTextField size="small" fullWidth value={params.row.amount} currencySymbol="$" disabled />;
}},
{ field: 'requestDate', headerName: 'Request Date', flex:0.15,
renderCell: (params) =>{
let formattedTime = ""
const date = new Date(params.row.requestDate);
formattedTime = date.toLocaleTimeString('en-US', { month: 'long', day: '2-digit', year: 'numeric', hour: '2-digit', minute: '2-digit' }); // Example format
return <DateField
size='small'
disabled
defaultValue={dayjs(params.row.requestDate)}
format="LL"
/>
} },
{ field: 'action', headerName: '', flex:0.15,
{ field: 'action', headerName: '', flex:0.1, filterable: false, sortable: false,
renderCell: (params) =>{
const acceptRequest = async () => {
let response = await fetch('/api/artist/requests/'+params.row["id"]+"/accept", { method: 'PUT' })
if(response.status === 200){
router.reload()
router.push("/dashboard/artist/requests/"+params.row["id"])
}
else{
alert("Error accepting request.")
@ -91,7 +75,7 @@ export default function ServerPaginationGrid() {
const denyRequest = async () => {
let response = await fetch('/api/artist/requests/'+params.row["id"]+"/deny", { method: 'PUT' })
if(response.status === 200){
router.reload()
router.push("/dashboard/artist/requests/"+params.row["id"])
}
else{
alert("Error accepting request.")
@ -101,7 +85,7 @@ export default function ServerPaginationGrid() {
const completeRequest = async () => {
let response = await fetch('/api/artist/requests/'+params.row["id"]+"/complete", { method: 'PUT' })
if(response.status === 200){
router.reload()
router.push("/dashboard/artist/requests/"+params.row["id"])
}
else{
alert("Error accepting request.")
@ -123,102 +107,13 @@ export default function ServerPaginationGrid() {
formattedTime = date.toLocaleTimeString('en-US', { month: 'long', day: '2-digit', year: 'numeric', hour: '2-digit', minute: '2-digit' }); // Example format
return (<>
<Dialog
fullWidth={true}
maxWidth={"lg"}
open={open}
onClose={handleClose}
>
<DialogTitle>Request submitted on {formattedTime ?? ''}</DialogTitle>
<DialogContent>
<Grid container spacing={3} sx={{paddingTop:"1%"}}>
<Grid item xs={12} md={6}>
<Grid container spacing={3}>
<Grid item xs={12} md={12}>
<TextField
multiline={true}
rows={10}
fullWidth
label="Request Message"
value={params.row.message}
disabled
/>
</Grid>
<Grid item xs={12} md={12}>
<Card>
<CardContent>
<Grid container>
<Grid item xs={12} md={12}>
</Grid>
<Grid item xs={12} md={12}>
</Grid>
</Grid>
</CardContent>
</Card>
</Grid>
</Grid>
</Grid>
<Grid item xs={12} md={6}>
<Grid container spacing={3}>
<Grid item xs={12} md={12}>
<Tooltip title="Decline this request.">
<IconButton onClick={denyRequest} disabled={params.row.declined || params.row.accepted} color="error"><Close/></IconButton>
</Tooltip>
<Tooltip title="Accept this request.">
<IconButton onClick={acceptRequest} disabled={params.row.declined || params.row.accepted} color="success"><Check/></IconButton>
</Tooltip>
<Tooltip title="Upload asset image for customer.">
<IconButton disabled={!params.row.paid} color="primary"><Upload/></IconButton>
</Tooltip>
<Tooltip title="Complete this request.">
<IconButton disabled={!params.row.paid || params.row.completed} color="success"><AssignmentTurnedInIcon/></IconButton>
</Tooltip>
{(!params.row.declined && !params.row.accepted && !params.row.paid && !params.row.completed ? (
<Chip icon={<Refresh />} label="Pending" variant="filled" color="secondary" />
):null)}
{(params.row.declined ? (
<Chip icon={<AssignmentLateIcon />} label="Declined" variant="filled" color="error" />
):null)}
{(params.row.accepted ? (
<Chip icon={<AssignmentTurnedInIcon />} label="Accepted" variant="filled" color="info" />
):null)}
{(params.row.paid ? (
<Chip icon={<PriceCheckIcon />} label="Paid" variant="filled" color="success" />
):null)}
{(params.row.paid==false && params.row.accepted ? (
<Chip icon={<PriceCheckIcon />} label="Pending Payment" variant="filled" color="warning" />
):null)}
{(params.row.completed ? (
<Chip disabled={!params.row.completed} icon={<Check />} label="Completed" variant="outlined" color="success" />
):null)}
</Grid>
<Grid item xs={12} md={12}>
<Card>
<CardContent>
<Grid container>
<Grid item xs={12} md={12}>
</Grid>
<Grid item xs={12} md={12}>
</Grid>
</Grid>
</CardContent>
</Card>
</Grid>
</Grid>
</Grid>
</Grid>
</DialogContent>
<DialogActions>
<Button onClick={handleClose}>Close</Button>
</DialogActions>
</Dialog>
<Tooltip title="View more details."><IconButton onClick={viewRequest} aria-label="accept" color="primary" onClick={handleClickOpen}><OpenInNew/></IconButton></Tooltip>
<Tooltip arrow title="View more details."><IconButton onClick={() => { router.push("/dashboard/artist/requests/"+params.row["id"])}} aria-label="accept" color="primary" ><OpenInNew/></IconButton></Tooltip>
{((params.row.accepted==false && params.row.declined==false && params.row.completed==false) ? (
<>
<Tooltip title="Accept this request.">
<Tooltip arrow title="Accept this request.">
<IconButton onClick={acceptRequest} aria-label="accept" color="success"><Check/></IconButton>
</Tooltip>
<Tooltip title="Deny this request.">
<Tooltip arrow title="Deny this request.">
<IconButton onClick={denyRequest} aria-label="deny" sx={{marginLeft:"2px"}} color="error"><Close/></IconButton>
</Tooltip>
</>
@ -226,7 +121,7 @@ export default function ServerPaginationGrid() {
)}
{((params.row.accepted==true && params.row.declined==false && params.row.completed==false && params.row.paid==true) ? (
<>
<Tooltip title="Complete this request.">
<Tooltip arrow title="Complete this request.">
<IconButton onClick={completeRequest} aria-label="complete" color="success"><ClipboardCheck/></IconButton>
</Tooltip>
</>
@ -236,12 +131,135 @@ export default function ServerPaginationGrid() {
)
} }
];
}
else{
columns = [
{ field: 'id', headerName: 'ID', flex: 0.1, filterable: false, sortable: false},
{ field: 'status', headerName: 'Status', flex: 0.15, filterable: false, sortable: false, filterable: false,
renderCell: (params) => {
if(params.row.completed){
return <Chip icon={<Check />} label="Completed" variant="outlined" color="success" />
}
else if(params.row.paid){
return <Chip icon={<PriceCheckIcon />} label="Paid" variant="outlined" color="success" />
}
else if(params.row.accepted && params.row.paid==false){
return <Chip icon={<PriceCheckIcon />} label="Pending Payment" variant="outlined" color="warning" />
}
else if(params.row.accepted && params.row.paid){
return <Chip icon={<AssignmentTurnedInIcon />} label="Accepted" variant="outlined" color="info" />
}
else if(params.row.declined){
return <Chip icon={<AssignmentLateIcon />} label="Declined" variant="outlined" color="error" />
}
else{
return <Chip icon={<Refresh />} label="Pending" variant="outlined" color="secondary" />
}
}
},
{ field: 'message', headerName: 'Message', flex: 0.5, filterable: false, sortable: false,
renderCell: (params) => {
return <TextField size="small" fullWidth value={params.row.message} disabled />;
}},
{ field: 'amount', headerName: 'Amount', flex: 0.1, filterable: false, sortable: false, renderCell: (params) => {
return <CurrencyTextField size="small" fullWidth value={params.row.amount} currencySymbol="$" disabled />;
}},
{ field: 'requestDate', headerName: 'Request Date', flex:0.15, filterable: false, sortable: false,
renderCell: (params) =>{
let formattedTime = ""
const date = new Date(params.row.requestDate);
formattedTime = date.toLocaleTimeString('en-US', { month: 'long', day: '2-digit', year: 'numeric', hour: '2-digit', minute: '2-digit' }); // Example format
return <DateField
size='small'
disabled
defaultValue={dayjs(params.row.requestDate)}
format="LL"
/>
} },
{ field: 'action', headerName: '', flex:0.15, filterable: false, sortable: false,
renderCell: (params) =>{
const acceptRequest = async () => {
let response = await fetch('/api/artist/requests/'+params.row["id"]+"/accept", { method: 'PUT' })
if(response.status === 200){
router.push("/dashboard/artist/requests/"+params.row["id"])
}
else{
alert("Error accepting request.")
}
}
const viewRequest = async () => {
}
const denyRequest = async () => {
let response = await fetch('/api/artist/requests/'+params.row["id"]+"/deny", { method: 'PUT' })
if(response.status === 200){
router.push("/dashboard/artist/requests/"+params.row["id"])
}
else{
alert("Error accepting request.")
}
}
const completeRequest = async () => {
let response = await fetch('/api/artist/requests/'+params.row["id"]+"/complete", { method: 'PUT' })
if(response.status === 200){
router.push("/dashboard/artist/requests/"+params.row["id"])
}
else{
alert("Error accepting request.")
}
}
const [open, setOpen] = React.useState(false);
const handleClickOpen = () => {
setOpen(true);
};
const handleClose = () => {
setOpen(false);
};
let formattedTime = ""
const date = new Date(params.row.requestDate);
formattedTime = date.toLocaleTimeString('en-US', { month: 'long', day: '2-digit', year: 'numeric', hour: '2-digit', minute: '2-digit' }); // Example format
return (<>
<Tooltip arrow title="View more details."><IconButton onClick={() => { router.push("/dashboard/artist/requests/"+params.row["id"])}} aria-label="accept" color="primary" ><OpenInNew/></IconButton></Tooltip>
{((params.row.accepted==false && params.row.declined==false && params.row.completed==false) ? (
<>
<Tooltip arrow title="Accept this request.">
<IconButton onClick={acceptRequest} aria-label="accept" color="success"><Check/></IconButton>
</Tooltip>
<Tooltip arrow title="Deny this request.">
<IconButton onClick={denyRequest} aria-label="deny" sx={{marginLeft:"2px"}} color="error"><Close/></IconButton>
</Tooltip>
</>
): null
)}
{((params.row.accepted==true && params.row.declined==false && params.row.completed==false && params.row.paid==true) ? (
<>
<Tooltip arrow title="Complete this request.">
<IconButton onClick={completeRequest} aria-label="complete" color="success"><ClipboardCheck/></IconButton>
</Tooltip>
</>
): null
)}
</>
)
} }
];
}
const [isLoading, setIsLoading] = React.useState(true);
const [requestCount, setRequestCount] = React.useState(null);
const [requestData, setRequestData] = React.useState({});
const [paginationModel, setPaginationModel] = React.useState({
page: 0,
pageSize: 5,
pageSize: 15,
});
@ -311,7 +329,7 @@ export default function ServerPaginationGrid() {
columns={columns}
rowCount={rowCountState}
loading={isLoading}
pageSizeOptions={[5]}
pageSizeOptions={[15]}
paginationModel={paginationModel}
paginationMode="server"
onPaginationModelChange={handlePageChange}

View File

@ -0,0 +1,311 @@
import * as React from 'react';
import { DataGrid } from '@mui/x-data-grid';
import { GridColDef } from '@mui/x-data-grid';
import TextField from '@mui/material/TextField';
import { Button, CardHeader, CircularProgress, Stack, Typography } from '@mui/material';
import CurrencyTextField from '@lupus-ai/mui-currency-textfield';
import { DateField } from '@mui/x-date-pickers/DateField';
import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider';
import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs';
import Chip from '@mui/material/Chip';
import {ArrowBack, Check, Close, Download, OpenInFull, OpenInNew, Refresh, Star, Upload } from '@mui/icons-material';
import PriceCheckIcon from '@mui/icons-material/PriceCheck';
import AssignmentTurnedInIcon from '@mui/icons-material/AssignmentTurnedIn';
import AssignmentLateIcon from '@mui/icons-material/AssignmentLate';
import ShoppingCartCheckoutIcon from '@mui/icons-material/ShoppingCartCheckout';
import { IconButton } from '@mui/material';
import Tooltip from '@mui/material/Tooltip';
import { Card, CardContent } from '@mui/material';
import Rating from '@mui/material/Rating';
import { Dialog, DialogTitle, DialogContent, DialogContentText, DialogActions, FormControl, InputLabel, Box } from '@mui/material';
import { Grid } from '@mui/material';
import { useRouter } from 'next/router';
import { withPageAuthRequired } from '@auth0/nextjs-auth0/client'
import { useState, useEffect } from 'react';
import { Alert } from '@mui/material';
import { ImageList } from '@mui/material';
import AssetImage from '../../../../components/dashboard/artist/AssetImage'
import ReferenceImage from '../../../../components/dashboard/artist/ReferenceImage'
import { UploadBoxOutline } from 'mdi-material-ui';
const ArtistRequestDetails = () => {
const [references, setReferences] = useState([]);
const [assets, setAssets] = useState([]);
const [request, setRequest] = useState(null);
const router = useRouter();
const getData = async () => {
if(router.query.requestId!=null){
const response = await fetch("/api/requests/"+router.query.requestId+"/details");
const data = await response.json();
setRequest(data);
setRating(data.requestRating)
setReview(data.reviewMessage)
const requestResponse = await fetch("/api/artist/requests/"+router.query.requestId+"/references");
const requestJson = await requestResponse.json();
setReferences(requestJson);
const assetResponse = await fetch("/api/artist/requests/"+router.query.requestId+"/assets");
const assetJson = await assetResponse.json();
setAssets(assetJson);
}
}
const acceptRequest = async () => {
let response = await fetch('/api/artist/requests/'+request["id"]+"/accept", { method: 'PUT' })
if(response.status === 200){
router.reload()
}
else{
alert("Error accepting request.")
}
}
const viewRequest = async () => {
}
const denyRequest = async () => {
let response = await fetch('/api/artist/requests/'+request["id"]+"/deny", { method: 'PUT' })
if(response.status === 200){
router.reload()
}
else{
alert("Error accepting request.")
}
}
const completeRequest = async () => {
let response = await fetch('/api/artist/requests/'+request["id"]+"/complete", { method: 'PUT' })
if(response.status === 200){
router.reload()
}
else{
alert("Error accepting request.")
}
}
const handleReferenceUpload = async (event) =>{
const file = event.target.files[0];
const formData = new FormData();
formData.append('newImage', file);
fetch('/api/artist/requests/'+router.query.requestId+"/newasset", {
method: 'POST',
body: formData // Don't set Content-Type, FormData will handle it
})
.then(response => response.json())
.then(data => {
getData();
})
.catch(error => {
console.error('Error uploading file:', error);
// Handle error appropriately
});
}
useEffect(() => {
getData()
}, [router.query.requestId]);
const [open, setOpen] = React.useState(false);
const [rating, setRating] = React.useState(1);
const [review, setReview] = React.useState("");
const handlePay = async () => {
var paymentUrlRequest = await fetch('/api/requests/'+request.id+'/payment')
//console.log(paymentUrlRequest);
var paymentUrlJson = await paymentUrlRequest.json();
var paymentUrl = paymentUrlJson.paymentUrl;
window.open(paymentUrl);
}
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 (<>
{(request) ? (
<Card>
<CardContent>
<Grid container spacing={3} sx={{paddingTop:"1%"}}>
<Grid item xs={12} md={6}>
<Grid container spacing={3}>
<Grid item xs={12} md={12}>
<TextField
multiline={true}
rows={10}
fullWidth
label="Request Message"
value={request.message}
disabled
/>
</Grid>
<Grid item xs={12} md={12}>
<Grid container>
<Grid item xs={12} md={12}>
<ImageList variant="masonry">
{(references.map((reference) => (
<ReferenceImage referenceId={reference.id} requestId={request.id}/>
)))}
</ImageList>
</Grid>
<Grid item xs={12} md={12}>
</Grid>
</Grid>
</Grid>
</Grid>
</Grid>
<Grid item xs={12} md={6}>
<Grid container spacing={3}>
<Grid item xs={12} md={10}>
<Grid container >
<Grid item xs={12} md={6}>
<Tooltip arrow title="Decline this request.">
<IconButton onClick={denyRequest} disabled={request.declined || request.accepted} color="error"><Close/></IconButton>
</Tooltip>
<Tooltip arrow title="Accept this request.">
<IconButton onClick={acceptRequest} disabled={request.declined || request.accepted} color="success"><Check/></IconButton>
</Tooltip>
<label htmlFor="uploadInput">
<Tooltip arrow title="Upload asset image for customer.">
<IconButton disabled={request.completed && request.paid} component="span" color="info"><UploadBoxOutline/></IconButton>
</Tooltip>
</label>
<Tooltip arrow title="Complete this request.">
<IconButton onClick={completeRequest} disabled={!request.paid || request.completed} color="success"><AssignmentTurnedInIcon/></IconButton>
</Tooltip>
</Grid>
<Grid item xs={12} md={6}>
<Stack spacing={2} direction="row">
{(request.declined ? (
<Tooltip title="The request has been declined.">
<Chip icon={<AssignmentLateIcon />} label="Declined" variant="outlined" color="error" />
</Tooltip>
):null)}
{(!request.declined && !request.accepted && !request.paid && !request.completed ? (
<Tooltip title="The request is pending.">
<Chip icon={<Refresh />} label="Pending" variant="outlined" color="secondary" />
</Tooltip>
):null)}
{(request.accepted && !request.completed ? (
<Tooltip title="The request has been accepted.">
<Chip icon={<AssignmentTurnedInIcon />} label="Accepted" variant="outlined" color="info" />
</Tooltip>
):null)}
{(request.paid && request.accepted ? (
<Tooltip title="The request has been paid for you are clear to work on the request!">
<Chip icon={<PriceCheckIcon />} label="Paid" variant="outlined" color="success" />
</Tooltip>
):null)}
{(request.paid==false && request.accepted ? (
<Tooltip title="The request has not been paid for.">
<Chip icon={<PriceCheckIcon />} label="Pending Payment" variant="outlined" color="warning" />
</Tooltip>
):null)}
{(request.completed ? (
<Tooltip title="The request has been completed.">
<Chip disabled={!request.completed} icon={<Check />} label="Completed" variant="outlined" color="success" />
</Tooltip>
):null)}
</Stack>
</Grid>
</Grid>
</Grid>
<Grid item xs={12} md={2} sx={{textAlign:"right"}}>
<Tooltip title="Go back to viewing all your requests.">
<IconButton onClick={() => {router.push("/dashboard/artist/requests")}} color="primary">
<ArrowBack/>
</IconButton>
</Tooltip>
</Grid>
<Grid item xs={12} md={12}>
<Grid container>
<Grid item xs={12} md={12}>
<Alert icon={<Check />} severity="info">
Request submitted on {formattedTime}
</Alert>
</Grid>
<Grid item xs={12} md={12}>
</Grid>
</Grid>
</Grid>
<Grid item xs={12} md={12}>
<Grid container spacing={3}>
<Grid item xs={12} md={12}>
{request.paid ? (
<>
<Grid container spacing={3}>
<Grid item xs={12} md={12}>
<Alert sx={{width:"100%"}} severity="success">The request has been paid for, start working on it!</Alert>
</Grid>
<Grid item xs={2} md={1}>
<input
id="uploadInput"
style={{ display: 'none' }}
accept="image/*"
type="file"
onChange={handleReferenceUpload}
disabled={request.completed}
/>
<label htmlFor="uploadInput">
<Tooltip arrow title="Upload a new reference image.">
{assets.length>0 ? (
<IconButton disabled={request.completed} component="span" color="info"><UploadBoxOutline sx={{fontSize:"2rem"}} /></IconButton>
):
(
<IconButton disabled={request.completed} component="span" color="warning"><UploadBoxOutline sx={{fontSize:"2rem"}} /></IconButton>
)}
</Tooltip>
</label>
</Grid>
<Grid item xs={10} md={11}>
{assets.length>0 ? (
<Alert sx={{width:"100%"}} severity="info">Your uploaded assets will appear below!</Alert>
):
(
<Alert sx={{width:"100%"}} severity="warning">You have not uploaded any assets!</Alert>
)}
</Grid>
<Grid item xs={12} md={12}>
</Grid>
</Grid>
</>
):(
<Alert sx={{width:"100%"}} severity="error">The request is not paid for do not work on the request!</Alert>
)}
</Grid>
<Grid item xs={12} md={12}>
<ImageList variant="masonry">
{(assets.map((asset) => (
<AssetImage assetId={asset.id} requestId={request.id}/>
)))}
</ImageList>
</Grid>
</Grid>
</Grid>
</Grid>
</Grid>
</Grid>
</CardContent>
</Card>
)
:(
<>
<Box sx={{paddingTop:"20%", textAlign:"center"}}>
<Typography variant="h6" component="h6" gutterBottom>
Loading...
</Typography>
<CircularProgress/>
</Box>
</>
)}
</>
);
};
// Protected route, checking user authentication client-side.(CSR)
export default withPageAuthRequired(ArtistRequestDetails);

View File

@ -0,0 +1,97 @@
import * as React from 'react';
import { DataGrid } from '@mui/x-data-grid';
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 ServerPaginationGrid() {
const router = useRouter();
const columns = [
{ field: 'requestId', headerName: 'Request ID', flex: 0.1},
{ field: 'message', headerName: 'Review Message', flex: 0.5},
{ field: 'rating', headerName: 'Rating', flex: 0.2, renderCell: (params) => {
return <Rating value={params.row.rating} readOnly />;
}},
{ field: 'reviewDate', headerName: 'Reviewed On', flex: 0.2, renderCell: (params) => {
let formattedTime = ""
const date = new Date(params.row.reviewDate);
formattedTime = date.toLocaleTimeString('en-US', { month: 'long', day: '2-digit', year: 'numeric', hour: '2-digit', minute: '2-digit' }); // Example format
return <DateField
size='small'
disabled
defaultValue={dayjs(params.row.reviewDate)}
format="LL"
/>
}
},
];
const [isLoading, setIsLoading] = React.useState(true);
const [reviewCount, setReviewCount] = React.useState(null);
const [reviewData, setReviewData] = React.useState({});
const [paginationModel, setPaginationModel] = React.useState({
page: 0,
pageSize: 15,
});
const getReviews = async () => {
setIsLoading(true);
const response = await fetch('/api/artist/reviews', {
method: 'GET',
headers: {
'Content-Type': 'application/json',
}
});
const data = await response.json();
setReviewData(data);
setIsLoading(false);
}
const getReviewsCount = async () => {
const response = await fetch('/api/artist/reviewscount', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
}
});
const data = await response.json();
setReviewCount(data);
setRowCountState((prevRowCountState) =>
data !== undefined
? data
: prevRowCountState,
);
return data;
}
// Some API clients return undefined while loading
// Following lines are here to prevent `rowCountState` from being undefined during the loading
const [rowCountState, setRowCountState] = React.useState(0);
React.useEffect(() => {
getReviews();
getReviewsCount();
}, [reviewCount, setRowCountState,paginationModel]);
return (
<div style={{ height: '100%', width: '100%' }}>
<LocalizationProvider dateAdapter={AdapterDayjs}>
<DataGrid
getRowId={(row) => row.requestId}
rows={reviewData}
columns={columns}
rowCount={rowCountState}
loading={isLoading}
pageSizeOptions={[15]}
paginationModel={paginationModel}
paginationMode="server"
onPaginationModelChange={setPaginationModel}
/>
</LocalizationProvider>
</div>
);
}

View File

@ -13,26 +13,29 @@ import CardStatisticsVerticalComponent from '../../core/components/card-statisti
// ** Styled Component Import
import ApexChartWrapper from '../../core/styles/libs/react-apexcharts'
import { Card, TextField, Typography } from '@mui/material'
import { Card, IconButton, TextField, Typography } from '@mui/material'
import { withApiAuthRequired } from "@auth0/nextjs-auth0";
import { withPageAuthRequired } from '@auth0/nextjs-auth0/client'
import Button from '@mui/material/Button'
import { CardContent } from '@mui/material'
import Onboarding from '../../components/Onboarding'
import Onboarding from '../../components/dashboard/Onboarding'
import { useState } from 'react'
import { useEffect } from 'react'
import router from 'next/router'
import { isObject } from 'util'
import Orders from '../../components/Orders'
import Orders from '../../components/dashboard/customer/orders'
import StatisticsCard from '../../views/dashboard/StatisticsCard'
import ArtistStats from '../../components/ArtistStats'
import { Clipboard } from 'mdi-material-ui'
import { ArrowDownBox, BankTransfer, Cash, Clipboard, CubeOutline, StarOutline } from 'mdi-material-ui'
import CircularProgress from '@mui/material/CircularProgress'
import Tooltip from '@mui/material/Tooltip'
import Box from '@mui/material/Box'
import { Fullscreen, List, OpenInBrowser, Settings, WebAsset } from '@mui/icons-material'
const Dashboard = () => {
const [userData , setUserData] = useState(null)
const [profileData, setArtistData] = useState(null);
const [requestData, setArtistRequestData] = useState(null);
const [onboardData, setOnboardedData] = useState(false);
@ -46,6 +49,9 @@ const Dashboard = () => {
}
const getData = async () => {
const userResponse = await fetch('/api/me');
const user = await userResponse.json();
setUserData(user);
const profileResponse = await fetch('/api/artist/profile');
const sellerProfile = await profileResponse.json();
setArtistData(sellerProfile);
@ -96,8 +102,10 @@ const Dashboard = () => {
</Grid>
<Grid item xs={12} md={4} sx={{textAlign:"center"}}>
</Grid>
<Grid item xs={12} md={5} sx={{textAlign:"center"}}>
<Button color="info" onClick={()=>{router.push("/dashboard/requests")}} fullWidth variant="contained">View All Requests</Button>
<Grid item xs={12} md={5} sx={{textAlign:"right"}}>
<Tooltip title="View all of your requests.">
<IconButton color="info" onClick={()=>{router.push("/dashboard/requests")}}><List/></IconButton>
</Tooltip>
</Grid>
<Grid item xs={12} md={12} sx={{paddingTop:"2%"}}>
<Orders />
@ -124,27 +132,41 @@ const Dashboard = () => {
null
)}
</Grid>
<Grid item xs={12} md={8}>
<TextField size="small" label="Your Public URL" variant="outlined" disabled fullWidth defaultValue={"http://localhost:3000/box/"+profileData["name"].replace(/\s+/g, '-') ?? ""}>http://localhost:3000/box/"{(profileData["name"].replace(/\s+/g, '-') ?? "")}</TextField>
<Grid item xs={12} md={12}>
<Tooltip title="View all of your reviews.">
<IconButton color="info" onClick={() => {router.push("/dashboard/artist/reviews") }} size="large">
<StarOutline style={{ fontSize: 64 }} />
</IconButton>
</Tooltip>
<Tooltip title="View all of your requests.">
<IconButton color="info" onClick={() => {router.push("/dashboard/artist/requests") }} size="large">
<List style={{ fontSize: 64 }} />
</IconButton>
</Tooltip>
<Tooltip title="View your payouts and request related transactions.">
<IconButton color="success" onClick={() => { router.push("/dashboard/artist/payout") }} size="large">
<BankTransfer style={{ fontSize: 64 }} />
</IconButton>
</Tooltip>
</Grid>
<Grid item xs={12} md={2}>
<Button color="info" startIcon={<Clipboard/>} onClick={()=>{router.push("/dashboard/artist/artistsettings#portfolio")}} fullWidth variant="outlined">Copy</Button>
</Grid>
<Grid item xs={12} md={2}>
<Button color="secondary" onClick={()=>{router.push("/dashboard/artist/artistsettings#portfolio")}} fullWidth variant="outlined">Preview</Button>
</Grid>
<Grid item xs={12} md={6}>
<Button color="secondary" onClick={()=>{router.push("/dashboard/artist/artistsettings#portfolio")}} fullWidth variant="contained">Manage Portfolio</Button>
</Grid>
<Grid item xs={12} md={6}>
<Button color="secondary" onClick={()=>{router.push("/dashboard/artist/pagesettings")}} fullWidth variant="contained">Design Store Page</Button>
</Grid>
<Grid item xs={12} md={6}>
<Button color="secondary" onClick={()=>{router.push("/dashboard/artist/pagesettings")}} fullWidth variant="contained">Payment Portal</Button>
</Grid>
<Grid item xs={12} md={6}>
<Button color="secondary" onClick={()=>{router.push("/dashboard/artist/artistsettings")}} fullWidth variant="contained">Configure Your Shop</Button>
<Grid item xs={12} md={12}>
<Tooltip title="Manage the settings of your artist account.">
<IconButton color="primary" onClick={() => { router.push("/dashboard/artist/artistsettings") }} size="large">
<Settings style={{ fontSize: 64 }} />
</IconButton>
</Tooltip>
<Tooltip title="Manage the settings of your artist page.">
<IconButton color="primary" onClick={() => { router.push("/dashboard/artist/pagesettings") }} size="large">
<WebAsset style={{ fontSize: 64 }} />
</IconButton>
</Tooltip>
<Tooltip title="Open your artist page.">
<IconButton color="primary" onClick={() => { router.push("/box/"+userData["displayName"]) }} size="large">
<OpenInBrowser style={{ fontSize: 64 }} />
</IconButton>
</Tooltip>
</Grid>
</Grid>
</CardContent>
</Card>

View File

@ -8,7 +8,7 @@ import { DateField } from '@mui/x-date-pickers/DateField';
import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider';
import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs';
import Chip from '@mui/material/Chip';
import {Check, Close, Download, OpenInFull, OpenInNew, Refresh, Upload } from '@mui/icons-material';
import {Check, Close, Download, OpenInFull, OpenInNew, Refresh, Star, Upload } from '@mui/icons-material';
import PriceCheckIcon from '@mui/icons-material/PriceCheck';
import AssignmentTurnedInIcon from '@mui/icons-material/AssignmentTurnedIn';
import AssignmentLateIcon from '@mui/icons-material/AssignmentLate';
@ -16,50 +16,110 @@ import ShoppingCartCheckoutIcon from '@mui/icons-material/ShoppingCartCheckout';
import { IconButton } from '@mui/material';
import Tooltip from '@mui/material/Tooltip';
import { Card, CardContent } from '@mui/material';
import Rating from '@mui/material/Rating';
import dayjs from 'dayjs';
import { DownloadBox } from 'mdi-material-ui';
import { DownloadBox, StarOutline } from 'mdi-material-ui';
import { Dialog, DialogTitle, DialogContent, DialogContentText, DialogActions, FormControl, InputLabel, Box } from '@mui/material';
import RequestDialog from '../../components/requestDialog';
import { Grid } from '@mui/material';
import { useRouter } from 'next/router';
import { request } from 'http';
import { useMediaQuery } from '@mui/material';
export default function ServerPaginationGrid() {
const isSmallScreen = useMediaQuery(theme => theme.breakpoints.down('sm')); // Check if the screen size is small
const router = useRouter();
const columns = [
{ field: 'id', headerName: 'ID', flex: 0.1},
{ field: 'status', headerName: 'Status', flex: 0.15,
let columns = [];
if(isSmallScreen){
columns = [
{ field: 'id', headerName: '', flex: 0.05, sortable: false, filterable: false},
{ field: 'status', headerName: 'Status', flex: 0.15, filterable: false, sortable: false,
renderCell: (params) => {
if(params.row.completed){
return <Chip icon={<Check />} label="Completed" variant="filled" color="success" />
return <Check color="success" />
}
else if(params.row.paid){
return <Chip icon={<PriceCheckIcon />} label="Paid" variant="filled" color="success" />
return <PriceCheckIcon color="success" />
}
else if(params.row.accepted && params.row.paid==false){
return <Chip icon={<PriceCheckIcon />} label="Pending Payment" variant="filled" color="warning" />
return <PriceCheckIcon color="warning" />
}
else if(params.row.accepted && params.row.paid){
return <Chip icon={<AssignmentTurnedInIcon />} label="Accepted" variant="filled" color="info" />
return <AssignmentTurnedInIcon color="info" />
}
else if(params.row.declined){
return <Chip icon={<AssignmentLateIcon />} label="Declined" variant="filled" color="error" />
return <AssignmentLateIcon color="error" />
}
else{
return <Chip icon={<Refresh />} label="Pending" variant="filled" color="secondary" />
return <Refresh color="secondary" />
}
}
},
{ field: 'message', headerName: 'Message', flex: 0.5,
{ field: 'amount', headerName: 'Amount', flex: 0.15, sortable: false, filterable: false, renderCell: (params) => {
return <CurrencyTextField size="small" fullWidth value={params.row.amount} currencySymbol="$" disabled />;
}},
{ field: 'actions', headerName: '', flex:0.1, sortable: false, filterable: false,
renderCell: (params) =>{
const handlePay = async () => {
var paymentUrlRequest = await fetch('/api/requests/'+params.row.id+'/payment')
//console.log(paymentUrlRequest);
var paymentUrlJson = await paymentUrlRequest.json();
var paymentUrl = paymentUrlJson.paymentUrl;
window.open(paymentUrl);
}
return (<>
<Tooltip arrow title="View more details."><IconButton onClick={() => { router.push("/dashboard/requests/"+params.row.id)}} aria-label="accept" color="primary"><OpenInNew/></IconButton></Tooltip>
{((params.row.accepted==true &&params.row.declined==false && params.row.paid==false) ? (
<Tooltip arrow title="Pay for this request."><IconButton onClick={handlePay} aria-label="accept" color="success"><ShoppingCartCheckoutIcon/></IconButton></Tooltip>
): null
)}
{((params.row.completed) ? (
<Tooltip arrow title="Download requests assets."><IconButton aria-label="download" color="secondary"><Download/></IconButton></Tooltip>
): null
)}
</>
)
} }
];
}
else{
columns = [
{ field: 'id', headerName: 'ID', flex: 0.1, sortable: false, filterable: false},
{ field: 'status', headerName: 'Status', flex: 0.15, sortable: false, filterable: false,
renderCell: (params) => {
if(params.row.completed){
return <Chip icon={<Check />} label="Completed" variant="outlined" color="success" />
}
else if(params.row.paid){
return <Chip icon={<PriceCheckIcon />} label="Paid" variant="outlined" color="success" />
}
else if(params.row.accepted && params.row.paid==false){
return <Chip icon={<PriceCheckIcon />} label="Pending Payment" variant="outlined" color="warning" />
}
else if(params.row.accepted && params.row.paid){
return <Chip icon={<AssignmentTurnedInIcon />} label="Accepted" variant="outlined" color="info" />
}
else if(params.row.declined){
return <Chip icon={<AssignmentLateIcon />} label="Declined" variant="outlined" color="error" />
}
else{
return <Chip icon={<Refresh />} label="Pending" variant="outlined" color="secondary" />
}
}
},
{ field: 'message', headerName: 'Message', flex: 0.5, sortable: false, filterable: false,
renderCell: (params) => {
return <TextField size="small" fullWidth value={params.row.message} disabled />;
}},
{ field: 'amount', headerName: 'Amount', flex: 0.1, renderCell: (params) => {
{ field: 'amount', headerName: 'Amount', flex: 0.1, filterable: false, renderCell: (params) => {
return <CurrencyTextField size="small" fullWidth value={params.row.amount} currencySymbol="$" disabled />;
}},
{ field: 'requestDate', headerName: 'Request Date', flex:0.15,
{ field: 'requestDate', headerName: 'Request Date', sortable: false, flex:0.15, filterable: false,
renderCell: (params) =>{
let formattedTime = ""
@ -72,41 +132,9 @@ export default function ServerPaginationGrid() {
format="LL"
/>
} },
{ field: 'download', headerName: '', flex:0.1,
{ field: 'download', headerName: '', sortable: false, flex:0.1, filterable: false,
renderCell: (params) =>{
const acceptRequest = async () => {
let response = await fetch('/api/artist/requests/'+params.row["id"]+"/accept", { method: 'PUT' })
if(response.status === 200){
router.reload()
}
else{
alert("Error accepting request.")
}
}
const viewRequest = async () => {
}
const denyRequest = async () => {
let response = await fetch('/api/artist/requests/'+params.row["id"]+"/deny", { method: 'PUT' })
if(response.status === 200){
router.reload()
}
else{
alert("Error accepting request.")
}
}
const completeRequest = async () => {
let response = await fetch('/api/artist/requests/'+params.row["id"]+"/complete", { method: 'PUT' })
if(response.status === 200){
router.reload()
}
else{
alert("Error accepting request.")
}
}
const handlePay = async () => {
var paymentUrlRequest = await fetch('/api/requests/'+params.row.id+'/payment')
//console.log(paymentUrlRequest);
@ -114,126 +142,27 @@ export default function ServerPaginationGrid() {
var paymentUrl = paymentUrlJson.paymentUrl;
window.open(paymentUrl);
}
const [open, setOpen] = React.useState(false);
const handleClickOpen = () => {
setOpen(true);
};
const handleClose = () => {
setOpen(false);
};
let formattedTime = ""
const date = new Date(params.row.requestDate);
formattedTime = date.toLocaleTimeString('en-US', { month: 'long', day: '2-digit', year: 'numeric', hour: '2-digit', minute: '2-digit' }); // Example format
return (<>
<Dialog
fullWidth={true}
maxWidth={"lg"}
open={open}
onClose={handleClose}
>
<DialogTitle>Request submitted on {formattedTime ?? ''}</DialogTitle>
<DialogContent>
<Grid container spacing={3} sx={{paddingTop:"1%"}}>
<Grid item xs={12} md={6}>
<Grid container spacing={3}>
<Grid item xs={12} md={12}>
<TextField
multiline={true}
rows={10}
fullWidth
label="Request Message"
value={params.row.message}
disabled
/>
</Grid>
<Grid item xs={12} md={12}>
<Card>
<CardContent>
<Grid container>
<Grid item xs={12} md={12}>
</Grid>
<Grid item xs={12} md={12}>
</Grid>
</Grid>
</CardContent>
</Card>
</Grid>
</Grid>
</Grid>
<Grid item xs={12} md={6}>
<Grid container spacing={3}>
<Grid item xs={12} md={12}>
<Tooltip title="Upload reference image.">
<IconButton disabled={params.row.accepted && params.row.paid} color="primary"><Upload/></IconButton>
</Tooltip>
<Tooltip title="Pay for this request.">
<IconButton onClick={handlePay} disabled={params.row.accepted && params.row.paid || params.row.declined} color="success"><ShoppingCartCheckoutIcon/></IconButton>
</Tooltip>
<Tooltip title="Download all assets.">
<IconButton disabled={!params.row.completed} color="secondary"><Download/></IconButton>
</Tooltip>
{(!params.row.declined && !params.row.accepted && !params.row.paid && !params.row.completed ? (
<Chip icon={<Refresh />} label="Pending" variant="filled" color="secondary" />
):null)}
{(params.row.declined ? (
<Chip icon={<AssignmentLateIcon />} label="Declined" variant="filled" color="error" />
):null)}
{(params.row.accepted ? (
<Chip icon={<AssignmentTurnedInIcon />} label="Accepted" variant="filled" color="info" />
):null)}
{(params.row.paid && params.row.acccepted ? (
<Chip label="Paid" variant="filled" color="success" />
):null)}
{(params.row.paid==false && params.row.accepted ? (
<Chip icon={<PriceCheckIcon />} label="Pending Payment" variant="filled" color="warning" />
):null)}
{(params.row.completed ? (
<Chip disabled={!params.row.completed} icon={<Check />} label="Completed" variant="outlined" color="success" />
):null)}
</Grid>
<Grid item xs={12} md={12}>
<Card>
<CardContent>
<Grid container>
<Grid item xs={12} md={12}>
</Grid>
<Grid item xs={12} md={12}>
</Grid>
</Grid>
</CardContent>
</Card>
</Grid>
</Grid>
</Grid>
</Grid>
</DialogContent>
<DialogActions>
<Button onClick={handleClose}>Close</Button>
</DialogActions>
</Dialog>
<Tooltip title="View more details."><IconButton onClick={handleClickOpen} aria-label="accept" color="primary"><OpenInNew/></IconButton></Tooltip>
<Tooltip arrow title="View more details."><IconButton onClick={() => { router.push("/dashboard/requests/"+params.row.id)}} aria-label="accept" color="primary"><OpenInNew/></IconButton></Tooltip>
{((params.row.accepted==true &&params.row.declined==false && params.row.paid==false) ? (
<Tooltip title="Pay for this request."><IconButton onClick={handlePay} aria-label="accept" color="success"><ShoppingCartCheckoutIcon/></IconButton></Tooltip>
<Tooltip arrow title="Pay for this request."><IconButton onClick={handlePay} aria-label="accept" color="success"><ShoppingCartCheckoutIcon/></IconButton></Tooltip>
): null
)}
{((params.row.completed) ? (
<Tooltip title="Download requests assets."><IconButton aria-label="download" color="secondary"><Download/></IconButton></Tooltip>
<Tooltip arrow title="Download requests assets."><IconButton aria-label="download" color="secondary"><Download/></IconButton></Tooltip>
): null
)}
</>
)
} }
];
}
const [isLoading, setIsLoading] = React.useState(true);
const [requestCount, setRequestCount] = React.useState(null);
const [requestData, setRequestData] = React.useState({});
const [paginationModel, setPaginationModel] = React.useState({
page: 0,
pageSize: 5,
pageSize: 15,
});
@ -299,7 +228,7 @@ export default function ServerPaginationGrid() {
columns={columns}
rowCount={rowCountState}
loading={isLoading}
pageSizeOptions={[5]}
pageSizeOptions={[15]}
paginationModel={paginationModel}
paginationMode="server"
onPaginationModelChange={setPaginationModel}

View File

@ -1,280 +1,327 @@
import { withPageAuthRequired } from "@auth0/nextjs-auth0/client";
import Grid from '@mui/material/Grid';
import Card from '@mui/material/Card';
import CardActions from '@mui/material/CardActions';
import CardContent from '@mui/material/CardContent';
import CardMedia from '@mui/material/CardMedia';
import Button from '@mui/material/Button';
import Typography from '@mui/material/Typography';
import { useEffect, useState } from "react";
import { DataGrid, GridColDef, GridValueGetterParams } from '@mui/x-data-grid';
import Rating from '@mui/material/Rating';
import Dialog from '@mui/material/Dialog';
import DialogTitle from '@mui/material/DialogTitle';
import DialogContent from '@mui/material/DialogContent';
import DialogContentText from '@mui/material/DialogContentText';
import DialogActions from '@mui/material/DialogActions';
import CurrencyTextField from '@lupus-ai/mui-currency-textfield';
import * as React from 'react';
import { DataGrid } from '@mui/x-data-grid';
import { GridColDef } from '@mui/x-data-grid';
import TextField from '@mui/material/TextField';
import ArtistPortfolio from "../../../components/Old/artistPortfolio";
import { RouterNetwork } from "mdi-material-ui";
import { useRouter } from "next/router";
import { profile } from "console";
const Profile = () => {
const [profileData, setArtistData] = useState(null);
const [description, setDescription] = useState("");
const [guidelines, setGuidelines] = useState("");
const [requestMessage, setRequestMessage] = useState("");
const [requestPrice, setRequestPrice] = useState(100.00);
const handleRequestMessageChange = (event) => {
setRequestMessage(event.target.value);
}
const handleRequestPriceChange = (event) => {
setRequestPrice(event.target.value);
}
import { Alert, Button, CircularProgress, ImageList, ImageListItem, ListItem, Stack, Typography } from '@mui/material';
import CurrencyTextField from '@lupus-ai/mui-currency-textfield';
import { DateField } from '@mui/x-date-pickers/DateField';
import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider';
import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs';
import Chip from '@mui/material/Chip';
import {ArrowBack, AssignmentLateOutlined, Check, Close, Download, Error, OpenInFull, OpenInNew, Refresh, Star, Upload, Warning } from '@mui/icons-material';
import PriceCheckIcon from '@mui/icons-material/PriceCheck';
import AssignmentTurnedInIcon from '@mui/icons-material/AssignmentTurnedIn';
import AssignmentLateIcon from '@mui/icons-material/AssignmentLate';
import ShoppingCartCheckoutIcon from '@mui/icons-material/ShoppingCartCheckout';
import { IconButton } from '@mui/material';
import Tooltip from '@mui/material/Tooltip';
import { Card, CardContent } from '@mui/material';
import Rating from '@mui/material/Rating';
import { Dialog, DialogTitle, DialogContent, DialogContentText, DialogActions, FormControl, InputLabel, Box } from '@mui/material';
import { Grid } from '@mui/material';
import { useRouter } from 'next/router';
import { withPageAuthRequired } from '@auth0/nextjs-auth0/client'
import { useState, useEffect } from 'react';
import { UploadBoxOutline } from 'mdi-material-ui';
import AssetImage from '../../../components/dashboard/artist/AssetImage'
import ReferenceImage from '../../../components/dashboard/artist/ReferenceImage'
import Paper from '@mui/material/Paper';
const RequestDetails = () => {
const [request, setRequest] = useState(null);
const [references, setReferences] = useState([]);
const [assets, setAssets] = useState([]);
const router = useRouter();
const [open, setOpen] = useState(false);
const handleClickOpen = () => {
setOpen(true);
};
const handleClose = () => {
setOpen(false);
};
const submitRequest = async () => {
var payload = JSON.stringify({
"artistId": profileData["id"],
"message": requestMessage,
"amount": requestPrice
});
//console.log(payload)
const requestResponse = await fetch('/api/box/newRequest', {
method: 'POST',
body: payload
})
if(requestResponse.ok){
router.push("/dashboard/requests")
}
else{
alert("Error submitting request")
}
}
const getData = async () => {
if(router.query.artistName!=null){
const profileResponse = await fetch('/api/discovery/artist/'+router.query.artistName);
const sellerProfile = await profileResponse.json();
setArtistData(sellerProfile);
if(router.query.requestId!=null){
const response = await fetch("/api/requests/"+router.query.requestId+"/details");
const data = await response.json();
setRequest(data);
setRating(data.reviewRating)
setReview(data.reviewMessage)
setAlreadyReviewed(data.reviewed)
setDescription(sellerProfile["description"]);
setGuidelines(sellerProfile["requestGuidelines"]);
const requestResponse = await fetch("/api/requests/"+router.query.requestId+"/references");
const requestJson = await requestResponse.json();
setReferences(requestJson);
const assetsResponse = await fetch("/api/requests/"+router.query.requestId+"/assets");
const assetsJson = await assetsResponse.json();
setAssets(assetsJson);
}
}
const handleReferenceUpload = async (event) =>{
const file = event.target.files[0];
const formData = new FormData();
formData.append('newImage', file);
fetch('/api/requests/'+router.query.requestId+"/newreference", {
method: 'POST',
body: formData // Don't set Content-Type, FormData will handle it
})
.then(response => response.json())
.then(data => {
getData();
})
.catch(error => {
console.error('Error uploading file:', error);
// Handle error appropriately
});
}
useEffect(() => {
getData()
}, [router.query.artistName]);
}, [router.query.requestId]);
const columns: GridColDef[] = [
{
field: 'message',
headerName: 'Review',
flex: 0.75
const [open, setOpen] = React.useState(false);
const [rating, setRating] = React.useState(1);
const [review, setReview] = React.useState("");
const [alreadyReviewed, setAlreadyReviewed] = React.useState(true);
const handleReviewChange = (event) => {
setReview(event.target.value);
}
const handleRatingChange = async (event) => {
var rating = event.target.value;
var response = await fetch('/api/requests/'+request.id+'/review', {
method:"PUT",
headers: {
'Content-Type': 'application/json'
},
{
field: 'rating',
headerName: 'Rating',
flex: 0.25,
renderCell: (params: GridValueGetterParams) => (
<Rating name="read-only" value={params.value} readOnly />
),
},
];
body: JSON.stringify({
rating: rating,
message: review
})
});
if(response.ok){
router.reload();
}
else{
alert("Could not submit review!")
}
}
const handlePay = async () => {
var paymentUrlRequest = await fetch('/api/requests/'+request.id+'/payment')
//console.log(paymentUrlRequest);
var paymentUrlJson = await paymentUrlRequest.json();
var paymentUrl = paymentUrlJson.paymentUrl;
window.open(paymentUrl);
}
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
const rows = [
{ id: 1, message: 'Great work!', rating: 5 },
{ id: 2, message: 'BAD work!', rating: 1 },
{ id: 3, message: 'Okay work!', rating: 4 },
{ id: 4, message: 'Meh work!', rating: 2 },
{ id: 5, message: 'Great work!', rating: 5 },
{ id: 6, message: 'Mid work!', rating: 3 },
{ id: 7, message: 'HORRIBLE work!', rating: 1 },
];
return (<>
{(request) ? (
<Card>
<CardContent>
return (
<>
<Dialog
open={open}
onClose={handleClose}
PaperProps={{
component: 'form',
onSubmit: (event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault();
const formData = new FormData(event.currentTarget);
const formJson = Object.fromEntries((formData as any).entries());
const email = formJson.email;
////console.log(email);
handleClose();
},
}}
>
<DialogTitle>New Request</DialogTitle>
<DialogContent>
<DialogContentText>
Please read the guidelines of submitting a request before you proceed. You can do that by closing this popup and looking at the page behind.
</DialogContentText>
<Grid container spacing={3} sx={{paddingTop:"1%"}}>
<Grid item xs={12} md={6}>
<Grid container spacing={3}>
<Grid item xs={12} md={12}>
<Tooltip title="This is the message sent in with the request explaining what the customer wants.">
<TextField
autoFocus
required
margin="dense"
id="name"
name="message"
label="Request Message"
type="message"
fullWidth
variant="outlined"
multiline
multiline={true}
rows={10}
onChange={handleRequestMessageChange}
value={requestMessage}
/>
<CurrencyTextField
label="Price"
variant="standard"
currencySymbol="$"
outputFormat="number"
decimalCharacter="."
digitGroupSeparator=","
fullWidth
onChange={handleRequestPriceChange}
value={requestPrice}/>
</DialogContent>
<DialogActions>
<Button onClick={handleClose}>Cancel</Button>
<Button onClick={submitRequest} type="submit">Submit Request</Button>
</DialogActions>
</Dialog>
<Grid container spacing={2}>
value={request.message}
disabled
/>
</Tooltip>
</Grid>
<Grid item xs={12} md={12}>
<Card>
<CardMedia
sx={{ height: "250px",
display: "flex",
alignItems: "center",
justifyContent: "center",
flexDirection: "column"
}}
image="https://images.pexels.com/photos/255379/pexels-photo-255379.jpeg?cs=srgb&dl=pexels-miguel-%C3%A1-padri%C3%B1%C3%A1n-255379.jpg&fm=jpg"
title="green iguana"
>
<Typography variant="h5" align="center" gutterBottom>STORE HEADER</Typography>
</CardMedia>
<CardContent>
<Grid container spacing={2}>
<Grid container>
<Grid item xs={12} md={12}>
<Grid container>
<Grid item xs={12} md={1}>
<input
id="uploadInput"
style={{ display: 'none' }}
accept="image/*"
type="file"
onChange={handleReferenceUpload}
disabled={request.declined || request.accepted}
/>
<label htmlFor="uploadInput">
<Tooltip arrow title="Upload a new reference image.">
<IconButton disabled={request.declined || request.accepted} component="span" color="info"><UploadBoxOutline sx={{fontSize:"2rem"}} /></IconButton>
</Tooltip>
</label>
</Grid>
<Grid item xs={12} md={11}>
{request.accepted ? (
<Alert sx={{width:"100%"}} severity="warning">You can no longer upload reference images, request is accepted!</Alert>
):(
(references.length > 0) ? (
<Alert severity="info">
There is a maximum of 10 reference images per request.
</Alert>
):(
<Alert sx={{width:"100%"}} severity="error">You need to add reference images to your request!</Alert>
)
)}
</Grid>
</Grid>
</Grid>
<Grid item xs={12} md={12}>
<ImageList variant="masonry">
{(references.map((reference) => (
<ReferenceImage referenceId={reference.id} requestId={request.id}/>
)))}
</ImageList>
</Grid>
</Grid>
</Grid>
</Grid>
</Grid>
<Grid item xs={12} md={6}>
<Grid container spacing={2}>
<Grid item xs={12} md={12} >
<Card>
<CardContent>
<Typography variant="h5" align="center" gutterBottom>BIOGRAPHY HEADER</Typography>
<Typography variant="body2" align="center">
{description}
</Typography>
</CardContent>
</Card>
<Grid container spacing={3}>
<Grid item xs={12} md={10}>
<Grid container >
<Grid item xs={12} md={6}>
<Tooltip arrow title="Download all assets.">
<IconButton disabled={!request.completed} color="secondary"><Download/></IconButton>
</Tooltip>
<Tooltip arrow title="Pay for this request.">
<IconButton onClick={handlePay} disabled={!(request.accepted && !request.paid)} color="success"><ShoppingCartCheckoutIcon/></IconButton>
</Tooltip>
</Grid>
<Grid item xs={12} md={12} textAlign={"center"}>
<Card>
<CardContent>
<Grid item xs={12} md={6}>
<Stack spacing={2} direction="row">
{(request.declined ? (
<Tooltip title="The request has been declined.">
<Chip icon={<AssignmentLateOutlined />} label="Declined" variant="outlined" color="error" />
</Tooltip>
):null)}
{(!request.declined && !request.accepted && !request.paid && !request.completed ? (
<Tooltip title="The request is pending.">
<Chip icon={<Refresh />} label="Pending" variant="outlined" color="secondary" />
</Tooltip>
):null)}
{(request.accepted && !request.completed ? (
<Tooltip title="The request has been accepted.">
<Chip icon={<AssignmentTurnedInIcon />} label="Accepted" variant="outlined" color="info" />
</Tooltip>
):null)}
{(request.paid && request.accepted ? (
<Tooltip title="The request has been paid for and your request is being worked on.">
<Chip icon={<PriceCheckIcon />} label="Paid" variant="outlined" color="success" />
</Tooltip>
):null)}
{(request.paid==false && request.accepted ? (
<Tooltip title="You have not paid for this request.">
<Chip icon={<PriceCheckIcon />} label="Pending Payment" variant="outlined" color="warning" />
</Tooltip>
):null)}
{(request.completed ? (
<Tooltip title="The request has been completed.">
<Chip disabled={!request.completed} icon={<Check />} label="Completed" variant="outlined" color="success" />
</Tooltip>
):null)}
</Stack>
</Grid>
</Grid>
</Grid>
<Grid item xs={12} md={2} sx={{textAlign:"right"}}>
<Tooltip title="Go back to viewing all your requests.">
<IconButton onClick={() => {router.push("/dashboard/requests")}} color="primary">
<ArrowBack/>
</IconButton>
</Tooltip>
</Grid>
<Grid item xs={12} md={12}>
<Grid container>
<Grid item xs={12} md={12}>
<Typography variant="h5" align="center" gutterBottom>GUIDELINES HEADER</Typography>
<Typography variant="body2" align="center">
{guidelines}
</Typography>
<Alert icon={<Check />} severity="info">
Request submitted on {formattedTime}
</Alert>
</Grid>
<Grid item xs={12} md={12}>
<Typography variant="caption" color={"error"} align="center" sx={{marginTop:"4%"}}>
By clicking "Start New Request" you are agreeing to the terms above and to the terms of service.
</Typography>
<Typography variant="caption" color={"primary"} align="center" sx={{marginTop:"4%"}}>
[TERMS OF SERVICE]
</Typography>
</Grid>
</Grid>
</Grid>
<Grid item xs={12} md={12}>
<Button onClick={handleClickOpen} size="large" variant="contained" color="primary" sx={{marginTop:"2%"}}>Start New Request</Button>
</Grid>
</Grid>
</CardContent>
</Card>
</Grid>
<Grid container spacing={3}>
<Grid item xs={12} md={12}>
<Card>
<CardContent>
<Grid container>
<Grid item xs={12} md={12} >
<Typography variant="h5" align="center" gutterBottom>REVIEWS HEADER</Typography>
{request.completed ? (
<Alert sx={{width:"100%"}} severity="success">Your success is complete and you can access your assets below!</Alert>
):(
<Alert sx={{width:"100%"}} severity="info">When your request is completed your assets will appear below!</Alert>
)}
</Grid>
{request.completed ? (
<Tooltip title={alreadyReviewed ? "You have already reviewed this request." : "Leave a review for this request now that it is completed!"}>
<Grid item xs={12} md={12}>
<DataGrid
rows={rows}
columns={columns}
autoHeight={true}
initialState={{
pagination: {
paginationModel: {
pageSize: 5,
},
},
}}
pageSizeOptions={[5]}
<Paper elevation={3} sx={{padding:"1%"}}>
<Grid container spacing={3}>
<Grid item xs={12} md={9}>
<TextField
fullWidth
focused
placeholder='Leave a review...'
value={review}
variant="outlined"
size="small"
disabled={alreadyReviewed}
onChange={handleReviewChange}
/>
</Grid>
</Grid>
</CardContent>
</Card>
</Grid>
</Grid>
</Grid>
<Grid item xs={12} md={6} >
<Card>
<CardContent>
<Grid container>
<Grid item xs={12} md={12} >
<Typography variant="h5" align="center" gutterBottom>PORTFOLIO HEADER</Typography>
<Grid item xs={12} md={3}>
<Rating
name="simple-controlled"
size="large"
value={rating}
disabled={!review || alreadyReviewed}
onChange={handleRatingChange}/>
</Grid>
<Grid item xs={12} md={12}>
<Typography variant="body2" align="center">
{profileData!=null ? (
<ArtistPortfolio masonry={true} columns={3} artistId={profileData["id"]} />
{request.completed ? (
request.reviewed ? (
<Alert sx={{width:"100%"}} severity="info">You have reviewed this request so others know the quality of this artists work!</Alert>
):(
<Alert sx={{width:"100%"}} severity="warning">Please leave a review for this request so other users know the quality of this artists work!</Alert>
)
): null}
</Grid>
</Grid>
</Paper>
</Grid>
</Tooltip>
): null}
<Grid item xs={12} md={12}>
<ImageList variant="masonry">
{(assets.map((asset) => (
<AssetImage assetId={asset.id} requestId={request.id}/>
)))}
</ImageList>
</Grid>
</Grid>
</Grid>
</Grid>
</Grid>
</Grid>
</CardContent>
</Card>
)
:(
<>
<Box sx={{paddingTop:"20%", textAlign:"center"}}>
<Typography variant="h6" component="h6" gutterBottom>
Loading...
</Typography>
</Grid>
</Grid>
</CardContent>
</Card>
</Grid>
</Grid>
</CardContent>
</Card>
</Grid>
</Grid>
<CircularProgress/>
</Box>
</>
)}
</>
);
};
// Protected route, checking user authentication client-side.(CSR)
export default withPageAuthRequired(Profile);
export default withPageAuthRequired(RequestDetails);

View File

@ -1,5 +1,5 @@
import { useUser, withPageAuthRequired } from "@auth0/nextjs-auth0/client";
import { CircularProgress, Grid, Typography } from "@mui/material";
import { CircularProgress, Grid, IconButton, Typography } from "@mui/material";
import { useEffect, useState } from "react";
import Card from "@mui/material/Card";
import CardContent from "@mui/material/CardContent";
@ -7,6 +7,8 @@ import TextField from '@mui/material/TextField';
import Button from '@mui/material/Button';
import Box from '@mui/material/Box';
import * as React from 'react';
import { Save } from "@mui/icons-material";
import Tooltip from '@mui/material/Tooltip';
const ArtistSettings = () => {
@ -64,11 +66,9 @@ const ArtistSettings = () => {
<Typography variant="h5" gutterBottom>General Settings</Typography>
</Grid>
<Grid item xs={12} md={4} >
{(saved) ? (
<Button variant="contained" color="success" fullWidth onClick={saveChanges}>Saved</Button>
):(
<Button variant="contained" color="success" fullWidth onClick={saveChanges}>Save Changes</Button>
)}
<Tooltip title="Save changes">
<IconButton onClick={saveChanges} color="success" sx={{float:"right"}}><Save/></IconButton>
</Tooltip>
</Grid>
<Grid item xs={12} md={12} sx={{ paddingTop: "2%" }}>
<TextField id="outlined-basic" label="Display Name" onChange={handleDisplayNameChange} variant="outlined" fullWidth value={displayName} />

View File

@ -1,37 +0,0 @@
// ** MUI Imports
import Grid from '@mui/material/Grid'
// ** Styled Component
import DatePickerWrapper from '../../core/styles/libs/react-datepicker'
// ** Demo Components Imports
import FormLayoutsBasic from '../../views/form-layouts/FormLayoutsBasic'
import FormLayoutsIcons from '../../views/form-layouts/FormLayoutsIcons'
import FormLayoutsSeparator from '../../views/form-layouts/FormLayoutsSeparator'
import FormLayoutsAlignment from '../../views/form-layouts/FormLayoutsAlignment'
// ** Third Party Styles Imports
import 'react-datepicker/dist/react-datepicker.css'
const FormLayouts = () => {
return (
<DatePickerWrapper>
<Grid container spacing={6}>
<Grid item xs={12} md={6}>
<FormLayoutsBasic />
</Grid>
<Grid item xs={12} md={6}>
<FormLayoutsIcons />
</Grid>
<Grid item xs={12}>
<FormLayoutsSeparator />
</Grid>
<Grid item xs={12}>
<FormLayoutsAlignment />
</Grid>
</Grid>
</DatePickerWrapper>
)
}
export default FormLayouts

View File

@ -1,164 +0,0 @@
// ** MUI Imports
import Grid from '@mui/material/Grid'
import Card from '@mui/material/Card'
import Link from '@mui/material/Link'
import Button from '@mui/material/Button'
import Tooltip from '@mui/material/Tooltip'
import Typography from '@mui/material/Typography'
import CardContent from '@mui/material/CardContent'
/**
** Icons Imports:
* ! You need to import all the icons which come from the API or from your server and then add these icons in 'icons' variable.
* ! If you need all the icons from the library, use "import * as Icon from 'mdi-material-ui'"
* */
import Abacus from 'mdi-material-ui/Abacus'
import Account from 'mdi-material-ui/Account'
import AbTesting from 'mdi-material-ui/AbTesting'
import AccountBox from 'mdi-material-ui/AccountBox'
import AccountCog from 'mdi-material-ui/AccountCog'
import AbjadArabic from 'mdi-material-ui/AbjadArabic'
import AbjadHebrew from 'mdi-material-ui/AbjadHebrew'
import AbugidaThai from 'mdi-material-ui/AbugidaThai'
import AccessPoint from 'mdi-material-ui/AccessPoint'
import AccountCash from 'mdi-material-ui/AccountCash'
import AccountEdit from 'mdi-material-ui/AccountEdit'
import AccountAlert from 'mdi-material-ui/AccountAlert'
import AccountCheck from 'mdi-material-ui/AccountCheck'
import AccountChild from 'mdi-material-ui/AccountChild'
import AccountClock from 'mdi-material-ui/AccountClock'
import AccountGroup from 'mdi-material-ui/AccountGroup'
import AccountCancel from 'mdi-material-ui/AccountCancel'
import AccountCircle from 'mdi-material-ui/AccountCircle'
import AccessPointOff from 'mdi-material-ui/AccessPointOff'
import AccountConvert from 'mdi-material-ui/AccountConvert'
import AccountDetails from 'mdi-material-ui/AccountDetails'
import AccessPointPlus from 'mdi-material-ui/AccessPointPlus'
import AccessPointCheck from 'mdi-material-ui/AccessPointCheck'
import AccessPointMinus from 'mdi-material-ui/AccessPointMinus'
import AccountArrowLeft from 'mdi-material-ui/AccountArrowLeft'
import AccountCowboyHat from 'mdi-material-ui/AccountCowboyHat'
import AbugidaDevanagari from 'mdi-material-ui/AbugidaDevanagari'
import AccessPointRemove from 'mdi-material-ui/AccessPointRemove'
import AccountArrowRight from 'mdi-material-ui/AccountArrowRight'
import AccountBoxOutline from 'mdi-material-ui/AccountBoxOutline'
import AccountCogOutline from 'mdi-material-ui/AccountCogOutline'
import AccessPointNetwork from 'mdi-material-ui/AccessPointNetwork'
import AccountBoxMultiple from 'mdi-material-ui/AccountBoxMultiple'
import AccountCashOutline from 'mdi-material-ui/AccountCashOutline'
import AccountChildCircle from 'mdi-material-ui/AccountChildCircle'
import AccountEditOutline from 'mdi-material-ui/AccountEditOutline'
import AccountAlertOutline from 'mdi-material-ui/AccountAlertOutline'
import AccountCheckOutline from 'mdi-material-ui/AccountCheckOutline'
import AccountChildOutline from 'mdi-material-ui/AccountChildOutline'
import AccountClockOutline from 'mdi-material-ui/AccountClockOutline'
import AccountCancelOutline from 'mdi-material-ui/AccountCancelOutline'
import AccountCircleOutline from 'mdi-material-ui/AccountCircleOutline'
import AccessPointNetworkOff from 'mdi-material-ui/AccessPointNetworkOff'
import AccountConvertOutline from 'mdi-material-ui/AccountConvertOutline'
import AccountDetailsOutline from 'mdi-material-ui/AccountDetailsOutline'
import AccountArrowLeftOutline from 'mdi-material-ui/AccountArrowLeftOutline'
import AccountArrowRightOutline from 'mdi-material-ui/AccountArrowRightOutline'
import AccountBoxMultipleOutline from 'mdi-material-ui/AccountBoxMultipleOutline'
const icons = {
Abacus,
Account,
AbTesting,
AccountBox,
AccountCog,
AbjadArabic,
AbjadHebrew,
AbugidaThai,
AccessPoint,
AccountCash,
AccountEdit,
AccountAlert,
AccountCheck,
AccountChild,
AccountClock,
AccountGroup,
AccountCancel,
AccountCircle,
AccessPointOff,
AccountConvert,
AccountDetails,
AccessPointPlus,
AccessPointCheck,
AccessPointMinus,
AccountArrowLeft,
AccountCowboyHat,
AbugidaDevanagari,
AccessPointRemove,
AccountArrowRight,
AccountBoxOutline,
AccountCogOutline,
AccessPointNetwork,
AccountBoxMultiple,
AccountCashOutline,
AccountChildCircle,
AccountEditOutline,
AccountAlertOutline,
AccountCheckOutline,
AccountChildOutline,
AccountClockOutline,
AccountCancelOutline,
AccountCircleOutline,
AccessPointNetworkOff,
AccountConvertOutline,
AccountDetailsOutline,
AccountArrowLeftOutline,
AccountArrowRightOutline,
AccountBoxMultipleOutline
}
const Icons = () => {
const renderIconGrids = () => {
return Object.keys(icons).map(key => {
const IconTag = icons[key as keyof typeof icons]
return (
<Grid item key={key}>
<Tooltip arrow title={key} placement='top'>
<Card>
<CardContent sx={{ display: 'flex' }}>
<IconTag />
</CardContent>
</Card>
</Tooltip>
</Grid>
)
})
}
return (
<Grid container spacing={6}>
<Grid item xs={12}>
<Typography variant='h5'>
<Link href='https://materialdesignicons.com/' target='_blank'>
Material Design Icons
</Link>
</Typography>
<Typography variant='body2'>Material Design Icons from the Community</Typography>
</Grid>
<Grid item xs={12}>
<Grid container spacing={6}>
{renderIconGrids()}
</Grid>
</Grid>
<Grid item xs={12} sx={{ textAlign: 'center' }}>
<Button
target='_blank'
rel='noreferrer'
component={Link}
variant='contained'
href='https://materialdesignicons.com/'
>
View All Material Design Icons
</Button>
</Grid>
</Grid>
)
}
export default Icons

Some files were not shown because too many files have changed in this diff Show More