feat: rating/reviews

This commit is contained in:
Damien Ostler 2024-02-27 21:37:43 -05:00
parent 238477665d
commit aac4ec902f
33 changed files with 681 additions and 511 deletions

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,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

@ -44,19 +44,8 @@ export default function ServerPaginationGrid() {
{ field: 'amount', headerName: 'Amount', 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"
/>
} }
{ 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);

View File

@ -0,0 +1,143 @@
import * as React from 'react';
import { DataGrid, GridToolbar } from '@mui/x-data-grid';
import { GridColDef } from '@mui/x-data-grid';
import { Button, Stack, Typography } from '@mui/material';
import { Chip, IconButton, Tooltip } from '@mui/material';
import { AssignmentLateOutlined, AssignmentTurnedInOutlined, Check, Download, PriceCheck, PriceCheckOutlined, Refresh, ShoppingCartCheckout } from '@mui/icons-material';
import { Card, CardContent } from '@mui/material';
import Rating from '@mui/material/Rating';
import { Dialog, DialogTitle, DialogContent, DialogActions, Grid, TextField } from '@mui/material';
import { useRouter } from 'next/router';
import { useState, useEffect } from 'react';
export default function RequestDialog() {
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={6}>
<Grid container>
<Grid item xs={12} md={6}>
<Grid container>
<Grid item xs={12} md={3}>
<Tooltip arrow title="Pay for this request.">
<IconButton onClick={handlePay} disabled={params.row.accepted && params.row.paid || params.row.declined} color="success"><ShoppingCartCheckoutIcon/></IconButton>
</Tooltip>
</Grid>
<Grid item xs={12} md={3}>
<Tooltip arrow title="Download all assets.">
<IconButton disabled={!params.row.completed} color="secondary"><Download/></IconButton>
</Tooltip>
</Grid>
</Grid>
</Grid>
<Grid item xs={12} md={6}>
<Grid container spacing={1}>
<Grid item xs={12} md={12}>
{(!params.row.declined && !params.row.accepted && !params.row.paid && !params.row.completed ? (
<Chip icon={<Refresh />} label="Pending" variant="filled" color="secondary" />
):null)}
</Grid>
<Grid item xs={12} md={12}>
{(params.row.declined ? (
<Chip icon={<AssignmentLateOutlined />} label="Declined" variant="filled" color="error" />
):null)}
</Grid>
<Grid item xs={12} md={12}>
{(params.row.accepted ? (
<Chip icon={<AssignmentTurnedInOutlined />} label="Accepted" variant="filled" color="info" />
):null)}
</Grid>
<Grid item xs={12} md={12}>
{(params.row.paid && params.row.acccepted ? (
<Chip label="Paid" variant="filled" color="success" />
):null)}
</Grid>
<Grid item xs={12} md={12}>
{(params.row.paid==false && params.row.accepted ? (
<Chip icon={<PriceCheckOutlined />} label="Pending Payment" variant="filled" color="warning" />
):null)}
</Grid>
<Grid item xs={12} md={12}>
{(params.row.completed ? (
<Chip disabled={!params.row.completed} icon={<Check />} label="Completed" variant="filled" color="success" />
):null)}
</Grid>
</Grid>
</Grid>
</Grid>
</Grid>
<Grid item xs={12} md={6} sx={{textAlign:"center"}}>
<Tooltip arrow title="Rate this request.">
<Rating
sx={{paddingTop:"1%"}}
size='large'
// name="simple-controlled"
// value={value}
// onChange={(event, newValue) => {
// setValue(newValue);
// }}
/>
</Tooltip>
<Tooltip arrow title="Write a review for this request..">
<TextField size="small" rows={4} multiline />
</Tooltip>
</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>
);
}

View File

@ -1,9 +1,11 @@
import * as React from 'react';
import { ImageList, Box, Button, CircularProgress, Slider } from '@mui/material';
import { ImageList, Box, Button, CircularProgress, Slider, IconButton } from '@mui/material';
import { useEffect, useState } from "react";
import EditableArtistPortfolioImage from './editableArtistPortfolioImage';
import FileOpenIcon from '@mui/icons-material/FileOpen';
import { Tooltip } from '@mui/material';
import { Grid } from '@mui/material';
import { FileUpload } from '@mui/icons-material';
const EditableArtistPortfolio = ({ artistId }) => {
const [portfolioData, setPortfolioData] = useState([]);
const [columns, setColumns] = useState(2);
@ -51,7 +53,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 +62,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 />
</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 +80,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

@ -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>
);
}

View File

@ -0,0 +1,28 @@
import * as React from 'react';
import {useEffect } from "react";
import { ImageList, ImageListItem } from '@mui/material';
export default function RequestReferences({id}) {
const [open, setOpen] = React.useState(false);
const [refrences, setReferences] = React.useState([]);
const getData = async () => {
const response = await fetch('/api/requests/' + id + '/references');
const references = await response.json();
setReferences(references);
}
useEffect(() => {
getData();
}, []);
return (<ImageList>
{refrences.map((item) => (
<ImageListItem>
<img src={item.url} alt={item.title} />
</ImageListItem>
))}
</ImageList>
);
}

85
components/reviews.tsx Normal file
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';
import dayjs from 'dayjs';
import CurrencyTextField from '@lupus-ai/mui-currency-textfield';
import { DateField } from '@mui/x-date-pickers';
export default function Reviews({artistId}) {
const router = useRouter();
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 />;
}}
];
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();
setReviewData(data);
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

@ -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

@ -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

@ -7,8 +7,8 @@ import SettingsApplicationsIcon from '@mui/icons-material/SettingsApplications';
// ** 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, OpenInBrowser, Settings, WebAsset } from '@mui/icons-material'
import { useState, useEffect } from 'react'
const navigation = (): VerticalNavItemsType => {
@ -70,6 +70,11 @@ const navigation = (): VerticalNavItemsType => {
{
sectionTitle: 'Artist'
},
{
title: 'Request Reviews',
icon: StarOutline,
path: '/dashboard/artist/reviews'
},
{
title: 'Incoming Requests',
icon: CubeOutline,
@ -78,21 +83,21 @@ const navigation = (): VerticalNavItemsType => {
{
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"] : "")
}
);

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

@ -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,15 +3,30 @@ import { getAccessToken, withApiAuthRequired, getSession } from '@auth0/nextjs-a
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/Customer/'+requestId+'/Reference', {
headers: {
"Authorization": `Bearer ${accessToken}`,
'Content-Type': 'application/json'
},
method: req.method,
body: req.body
});
let result = await response.json();
res.status(200).json(result);
if(req.method == 'GET'){
const response = await fetch(process.env.NEXT_PUBLIC_API_URL+'/api/Requests/Customer/'+requestId+'/Reference', {
headers: {
"Authorization": `Bearer ${accessToken}`,
'Content-Type': 'application/json'
},
method: req.method
});
console.log(response)
let result = await response.json();
res.status(200).json(result);
}
else{
const response = await fetch(process.env.NEXT_PUBLIC_API_URL+'/api/Requests/Customer/'+requestId+'/Reference', {
headers: {
"Authorization": `Bearer ${accessToken}`,
'Content-Type': 'application/json'
},
method: req.method,
body: req.body
});
console.log(response)
let result = await response.json();
res.status(200).json(result);
}
});

View File

@ -0,0 +1,17 @@
import { getAccessToken, withApiAuthRequired, getSession } from '@auth0/nextjs-auth0';
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/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

@ -1,8 +1,8 @@
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/editableArtistPortfolio";
import { useEffect, useState } from "react";
import TextField from '@mui/material/TextField';
import Button from '@mui/material/Button';
@ -10,6 +10,7 @@ import Switch from '@mui/material/Switch';
import Divider from '@mui/material/Divider';
import ArtistRequest from "../../../components/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/editableArtistPortfolio";
import ArtistPortfolio from "../../../components/artistPortfolio";
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/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,21 +34,14 @@ 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}>
<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}>
<CurrencyTextField fullWidth label="Pending Balance" size="large" disabled variant="standard" currencySymbol="$" outputFormat="string" decimalCharacter="." digitGroupSeparator="," value={payoutData ? payoutData["pendingBalance"] : ""} />
</Grid>
</Grid>
<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={8}>
<Button fullWidth href={payoutData ? payoutData["payoutUrl"] : ""} target="_blank" startIcon={<OpenInNew/>} variant="contained" color="info" size="large">Open Dashboard</Button>
<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>
</CardContent>

View File

@ -21,9 +21,7 @@ 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 RequestReferences from '../../../components/requestReferences';
export default function ServerPaginationGrid() {
const router = useRouter();
@ -149,8 +147,10 @@ export default function ServerPaginationGrid() {
<CardContent>
<Grid container>
<Grid item xs={12} md={12}>
<Typography variant="h6" align="center">Reference Images</Typography>
</Grid>
<Grid item xs={12} md={12}>
<RequestReferences id={params.row.id} />
</Grid>
</Grid>
</CardContent>
@ -161,16 +161,16 @@ export default function ServerPaginationGrid() {
<Grid item xs={12} md={6}>
<Grid container spacing={3}>
<Grid item xs={12} md={12}>
<Tooltip title="Decline this request.">
<Tooltip arrow title="Decline this request.">
<IconButton onClick={denyRequest} disabled={params.row.declined || params.row.accepted} color="error"><Close/></IconButton>
</Tooltip>
<Tooltip title="Accept this request.">
<Tooltip arrow 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.">
<Tooltip arrow title="Upload asset image for customer.">
<IconButton disabled={!params.row.paid} color="primary"><Upload/></IconButton>
</Tooltip>
<Tooltip title="Complete this request.">
<Tooltip arrow 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 ? (
@ -189,7 +189,7 @@ export default function ServerPaginationGrid() {
<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" />
<Chip disabled={!params.row.completed} icon={<Check />} label="Completed" variant="filled" color="success" />
):null)}
</Grid>
<Grid item xs={12} md={12}>
@ -212,13 +212,13 @@ export default function ServerPaginationGrid() {
<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={viewRequest} aria-label="accept" color="primary" onClick={handleClickOpen}><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 +226,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>
</>
@ -241,7 +241,7 @@ export default function ServerPaginationGrid() {
const [requestData, setRequestData] = React.useState({});
const [paginationModel, setPaginationModel] = React.useState({
page: 0,
pageSize: 5,
pageSize: 15,
});
@ -311,7 +311,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,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,7 +13,7 @@ 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'
@ -26,13 +26,16 @@ import { isObject } from 'util'
import Orders from '../../components/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, 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="Open an expanded view of your requests .">
<IconButton color="info" onClick={()=>{router.push("/dashboard/requests")}}><Fullscreen/></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="Open an expanded view of your reviews.">
<IconButton color="info" onClick={() => {router.push("/dashboard/artist/reviews") }} size="large">
<StarOutline style={{ fontSize: 64 }} />
</IconButton>
</Tooltip>
<Tooltip title="Open an expanded view of your requests.">
<IconButton color="info" onClick={() => {router.push("/dashboard/artist/requests") }} size="large">
<CubeOutline 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,12 +16,12 @@ 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';
@ -115,7 +115,9 @@ export default function ServerPaginationGrid() {
window.open(paymentUrl);
}
const [open, setOpen] = React.useState(false);
const [rating, setRating] = React.useState(params.row.reviewRating);
const [review, setReview] = React.useState(params.row.reviewMessage);
const [alreadyReviewed, setAlreadyReviewed] = React.useState(params.row.reviewMessage != null && params.row.reviewMessage != "");
const handleClickOpen = () => {
setOpen(true);
};
@ -124,6 +126,31 @@ export default function ServerPaginationGrid() {
setOpen(false);
};
const handleReviewChange = (event) => {
setReview(event.target.value);
}
const handleRatingChange = async (event) => {
var rating = event.target.value;
var response = await fetch('/api/requests/'+params.row.id+'/review', {
method:"PUT",
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
rating: rating,
message: review
})
});
if(response.ok){
router.push("/dashboard/requests")
}
else{
alert("Could not submit review!")
}
}
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
@ -166,35 +193,74 @@ export default function ServerPaginationGrid() {
</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 item xs={12} md={6}>
<Grid container>
<Grid item xs={12} md={6}>
<Grid container>
<Grid item xs={12} md={3}>
<Tooltip arrow title="Pay for this request.">
<IconButton onClick={handlePay} disabled={params.row.accepted && params.row.paid || params.row.declined} color="success"><ShoppingCartCheckoutIcon/></IconButton>
</Tooltip>
</Grid>
<Grid item xs={12} md={3}>
<Tooltip arrow title="Download all assets.">
<IconButton disabled={!params.row.completed} color="secondary"><Download/></IconButton>
</Tooltip>
</Grid>
</Grid>
</Grid>
<Grid item xs={12} md={6}>
<Grid container spacing={1}>
<Grid item xs={12} md={12}>
{(!params.row.declined && !params.row.accepted && !params.row.paid && !params.row.completed ? (
<Chip icon={<Refresh />} label="Pending" variant="filled" color="secondary" />
):null)}
</Grid>
<Grid item xs={12} md={12}>
{(params.row.declined ? (
<Chip icon={<AssignmentLateIcon />} label="Declined" variant="filled" color="error" />
):null)}
</Grid>
<Grid item xs={12} md={12}>
{(params.row.accepted ? (
<Chip icon={<AssignmentTurnedInIcon />} label="Accepted" variant="filled" color="info" />
):null)}
</Grid>
<Grid item xs={12} md={12}>
{(params.row.paid && params.row.acccepted ? (
<Chip label="Paid" variant="filled" color="success" />
):null)}
</Grid>
<Grid item xs={12} md={12}>
{(params.row.paid==false && params.row.accepted ? (
<Chip icon={<PriceCheckIcon />} label="Pending Payment" variant="filled" color="warning" />
):null)}
</Grid>
<Grid item xs={12} md={12}>
{(params.row.completed ? (
<Chip disabled={!params.row.completed} icon={<Check />} label="Completed" variant="filled" color="success" />
):null)}
</Grid>
</Grid>
</Grid>
</Grid>
</Grid>
{(params.row.completed) ? (
<Grid item xs={12} md={6} sx={{textAlign:"center"}}>
<Tooltip arrow title="Rate this request.">
<Rating
sx={{paddingTop:"1%"}}
size='large'
value={rating}
onChange={handleRatingChange}
disabled={alreadyReviewed}
/>
</Tooltip>
<Tooltip arrow title={alreadyReviewed ? "The review you left for this request." : "Write a review for this request."}>
<TextField disabled={alreadyReviewed} onChange={handleReviewChange} size="small" value={params.row.reviewMessage} focused rows={4} multiline></TextField>
</Tooltip>
</Grid>
):null}
<Grid item xs={12} md={12}>
<Card>
<CardContent>
@ -215,13 +281,13 @@ export default function ServerPaginationGrid() {
<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={handleClickOpen} 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
)}
</>
@ -233,7 +299,7 @@ export default function ServerPaginationGrid() {
const [requestData, setRequestData] = React.useState({});
const [paginationModel, setPaginationModel] = React.useState({
page: 0,
pageSize: 5,
pageSize: 15,
});
@ -299,7 +365,7 @@ export default function ServerPaginationGrid() {
columns={columns}
rowCount={rowCountState}
loading={isLoading}
pageSizeOptions={[5]}
pageSizeOptions={[15]}
paginationModel={paginationModel}
paginationMode="server"
onPaginationModelChange={setPaginationModel}

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} />