feat: refactoring + assets and reference images and reviews

This commit is contained in:
Damien Ostler 2024-03-02 00:51:43 -05:00
parent d52018f7eb
commit 93d4518500
43 changed files with 1393 additions and 1319 deletions

View File

@ -1,143 +0,0 @@
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,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,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 './artistDashboardRequest';
import ArtistPortfolio from '../components/artistPortfolio';
import EditableArtistPortfolio from '../components/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

@ -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,11 +1,9 @@
import * as React from 'react';
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 { 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);
@ -64,7 +62,7 @@ const EditableArtistPortfolio = ({ artistId }) => {
<label htmlFor="portfolioUploadInput">
<Tooltip arrow title="Upload Image To Portfolio">
<IconButton color="primary" component="span">
<FileUpload />
<FileUpload sx={{fontSize:"2rem"}}/>
</IconButton>
</Tooltip>
</label>

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

@ -12,8 +12,8 @@ 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: '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 />;
}}

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,12 @@ 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 dayjs from 'dayjs';
export default function ServerPaginationGrid() {
export default function CustomerOrders() {
const columns = [
{ field: 'id', headerName: 'ID', flex: 0.1},
{ field: 'status', headerName: 'Status', flex: 0.15,

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,28 +0,0 @@
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>
);
}

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

@ -4,7 +4,7 @@ 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, StarOutline } from 'mdi-material-ui'
@ -37,6 +37,11 @@ const navigation = (): VerticalNavItemsType => {
}, []);
var result = [
{
title: 'Dashboard',
icon: HomeOutline,
path: '/dashboard'
},
{
sectionTitle: 'Admin'
},
@ -48,11 +53,6 @@ const navigation = (): VerticalNavItemsType => {
{
sectionTitle: 'General'
},
{
title: 'Dashboard',
icon: HomeOutline,
path: '/dashboard'
},
{
title: 'Account Settings',
icon: Settings,
@ -60,7 +60,7 @@ const navigation = (): VerticalNavItemsType => {
},
{
title: 'Your Requests',
icon: Cart,
icon: ListIcon,
path: '/dashboard/requests'
}
];
@ -77,7 +77,7 @@ const navigation = (): VerticalNavItemsType => {
},
{
title: 'Incoming Requests',
icon: CubeOutline,
icon: ListIcon,
path: '/dashboard/artist/requests'
},
{

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

@ -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+'/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 references(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

@ -0,0 +1,16 @@
import { getAccessToken, withApiAuthRequired, getSession } from '@auth0/nextjs-auth0';
export default withApiAuthRequired(async function requestDetails(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 references(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

@ -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) {
if (req.method === 'POST') {
const url = process.env.NEXT_PUBLIC_API_URL + `/api/Requests/Request`;
const { accessToken } = await getAccessToken(req, res);
export default async function handler(req, res ): Promise<any> {
const { artistName } = req.query;
var url = process.env.NEXT_PUBLIC_API_URL+`/api/Requests/Request`;
const { accessToken } = await getAccessToken(req, res);
const response = await fetch(url,{
method: 'POST',
headers: {
try {
const response = await fetch(url, {
method: 'POST',
headers: {
"Authorization": `Bearer ${accessToken}`,
'Content-Type': 'application/json'
},
body: req.body, // Pipe the incoming request directly to the outgoing request
});
"Authorization": `Bearer ${accessToken}`,
'Content-Type': 'application/json'
},
body: req.body
});
//console.log(response)
if (!response.ok) {
throw new Error('Failed to fetch seller');
if (!response.ok) {
const errorData = await response.json();
res.status(response.status).json(errorData);
return;
}
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' });
}
let result = await response.json();
res.status(200).json(result);
}
}

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/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 references(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 requestDetails(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,32 +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;
if(req.method == 'GET'){
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
});
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,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 references(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

@ -16,11 +16,12 @@ 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 = () => {
@ -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
const requestResponse = await fetch('/api/box/newRequest', {
method: 'POST',
body: formData // Pass FormData containing the file
});
if(requestResponse.ok){
router.push("/dashboard/requests")
} else {
alert("Error submitting request")
try {
const requestResponse = await fetch('/api/box/newRequest', {
method: 'POST',
body: JSON.stringify({
artistId: profileData["id"],
message: payload.get('Message'),
amount: payload.get('Amount'),
})
});
if (requestResponse.ok) {
const requestResponseData = await requestResponse.json();
router.push("/dashboard/requests/"+requestResponseData["id"]);
} else {
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,39 +134,17 @@ const Profile = () => {
value={requestMessage}
/>
<CurrencyTextField
label="Price"
label="Amount"
variant="standard"
currencySymbol="$"
name="Amount"
outputFormat="number"
decimalCharacter="."
digitGroupSeparator=","
fullWidth
onChange={handleRequestPriceChange}
value={requestPrice}
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>

View File

@ -8,7 +8,7 @@ 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";
const ArtistRequests = () => {
const {user, isLoading} = useUser();

View File

@ -2,13 +2,13 @@ import { useUser,withPageAuthRequired } from "@auth0/nextjs-auth0/client";
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/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";

View File

@ -7,13 +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/editableArtistPortfolio";
import ArtistPortfolio from "../../../components/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/reviews";
import Reviews from "../../../components/dashboard/artist/reviews";
const Profile = () => {

View File

@ -30,22 +30,22 @@ export default function ServerPaginationGrid() {
{ field: 'status', headerName: 'Status', flex: 0.15,
renderCell: (params) => {
if(params.row.completed){
return <Chip icon={<Check />} label="Completed" variant="filled" color="success" />
return <Chip icon={<Check />} label="Completed" variant="outlined" color="success" />
}
else if(params.row.paid){
return <Chip icon={<PriceCheckIcon />} label="Paid" variant="filled" color="success" />
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="filled" color="warning" />
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="filled" color="info" />
return <Chip icon={<AssignmentTurnedInIcon />} label="Accepted" variant="outlined" color="info" />
}
else if(params.row.declined){
return <Chip icon={<AssignmentLateIcon />} label="Declined" variant="filled" color="error" />
return <Chip icon={<AssignmentLateIcon />} label="Declined" variant="outlined" color="error" />
}
else{
return <Chip icon={<Refresh />} label="Pending" variant="filled" color="secondary" />
return <Chip icon={<Refresh />} label="Pending" variant="outlined" color="secondary" />
}
}
},
@ -75,7 +75,7 @@ export default function ServerPaginationGrid() {
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.")
@ -89,7 +89,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.")
@ -99,7 +99,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.")
@ -121,98 +121,7 @@ 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}>
<Typography variant="h6" align="center">Reference Images</Typography>
</Grid>
<Grid item xs={12} md={12}>
<RequestReferences id={params.row.id} />
</Grid>
</Grid>
</CardContent>
</Card>
</Grid>
</Grid>
</Grid>
<Grid item xs={12} md={6}>
<Grid container spacing={3}>
<Grid item xs={12} md={12}>
<Tooltip arrow title="Decline this request.">
<IconButton onClick={denyRequest} disabled={params.row.declined || params.row.accepted} color="error"><Close/></IconButton>
</Tooltip>
<Tooltip arrow title="Accept this request.">
<IconButton onClick={acceptRequest} disabled={params.row.declined || params.row.accepted} color="success"><Check/></IconButton>
</Tooltip>
<Tooltip arrow title="Upload asset image for customer.">
<IconButton disabled={!params.row.paid} color="primary"><Upload/></IconButton>
</Tooltip>
<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 ? (
<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="filled" 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 arrow 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 arrow title="Accept this request.">

View File

@ -0,0 +1,310 @@
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} 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>
)
:(
<>
<Typography variant="h6" component="h6" gutterBottom>
Loading...
</Typography>
<Box sx={{paddingTop:"20%"}}></Box>
<CircularProgress/>
</>
)}
</>
);
};
// Protected route, checking user authentication client-side.(CSR)
export default withPageAuthRequired(ArtistRequestDetails);

View File

@ -18,19 +18,19 @@ 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 { 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'
import { Fullscreen, List, OpenInBrowser, Settings, WebAsset } from '@mui/icons-material'
@ -103,8 +103,8 @@ const Dashboard = () => {
<Grid item xs={12} md={4} sx={{textAlign:"center"}}>
</Grid>
<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 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%"}}>
@ -133,14 +133,14 @@ const Dashboard = () => {
)}
</Grid>
<Grid item xs={12} md={12}>
<Tooltip title="Open an expanded view of your reviews.">
<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="Open an expanded view of your requests.">
<Tooltip title="View all of your requests.">
<IconButton color="info" onClick={() => {router.push("/dashboard/artist/requests") }} size="large">
<CubeOutline style={{ fontSize: 64 }} />
<List style={{ fontSize: 64 }} />
</IconButton>
</Tooltip>
<Tooltip title="View your payouts and request related transactions.">

View File

@ -24,6 +24,7 @@ import { DownloadBox, 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';
export default function ServerPaginationGrid() {
@ -33,22 +34,22 @@ export default function ServerPaginationGrid() {
{ field: 'status', headerName: 'Status', flex: 0.15,
renderCell: (params) => {
if(params.row.completed){
return <Chip icon={<Check />} label="Completed" variant="filled" color="success" />
return <Chip icon={<Check />} label="Completed" variant="outlined" color="success" />
}
else if(params.row.paid){
return <Chip icon={<PriceCheckIcon />} label="Paid" variant="filled" color="success" />
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="filled" color="warning" />
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="filled" color="info" />
return <Chip icon={<AssignmentTurnedInIcon />} label="Accepted" variant="outlined" color="info" />
}
else if(params.row.declined){
return <Chip icon={<AssignmentLateIcon />} label="Declined" variant="filled" color="error" />
return <Chip icon={<AssignmentLateIcon />} label="Declined" variant="outlined" color="error" />
}
else{
return <Chip icon={<Refresh />} label="Pending" variant="filled" color="secondary" />
return <Chip icon={<Refresh />} label="Pending" variant="outlined" color="secondary" />
}
}
},
@ -74,214 +75,16 @@ export default function ServerPaginationGrid() {
} },
{ field: 'download', headerName: '', flex:0.1,
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);
var paymentUrlJson = await paymentUrlRequest.json();
var paymentUrl = paymentUrlJson.paymentUrl;
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);
};
const handleClose = () => {
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
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 (<>
<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={<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>
<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 arrow 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 arrow title="Pay for this request."><IconButton onClick={handlePay} aria-label="accept" color="success"><ShoppingCartCheckoutIcon/></IconButton></Tooltip>
): null

View File

@ -1,280 +1,317 @@
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.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)
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 getData = async () => {
if(router.query.artistName!=null){
const profileResponse = await fetch('/api/discovery/artist/'+router.query.artistName);
const sellerProfile = await profileResponse.json();
setArtistData(sellerProfile);
const handleReferenceUpload = async (event) =>{
const file = event.target.files[0];
const formData = new FormData();
formData.append('newImage', file);
setDescription(sellerProfile["description"]);
setGuidelines(sellerProfile["requestGuidelines"]);
}
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
},
{
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 },
];
const [open, setOpen] = React.useState(false);
const [rating, setRating] = React.useState(1);
const [review, setReview] = React.useState("");
const [alreadyReviewed, setAlreadyReviewed] = React.useState(true);
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>
<TextField
autoFocus
required
margin="dense"
id="name"
name="message"
label="Request Message"
type="message"
fullWidth
variant="outlined"
multiline
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>
const handleReviewChange = (event) => {
setReview(event.target.value);
}
<Grid container spacing={2}>
<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 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>
<Grid item xs={12} md={12} textAlign={"center"}>
<Card>
<CardContent>
<Grid container>
<Grid item xs={12} md={12} >
<Typography variant="h5" align="center" gutterBottom>GUIDELINES HEADER</Typography>
<Typography variant="body2" align="center">
{guidelines}
</Typography>
</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 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 item xs={12} md={12} >
<Card>
<CardContent>
<Grid container>
<Grid item xs={12} md={12} >
<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]}
/>
</Grid>
</Grid>
</CardContent>
</Card>
</Grid>
</Grid>
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'
},
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
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}>
<Tooltip title="This is the message sent in with the request explaining what the customer wants.">
<TextField
multiline={true}
rows={10}
fullWidth
value={request.message}
disabled
/>
</Tooltip>
</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>
<Grid item xs={12} md={12} >
<Typography variant="body2" align="center">
{profileData!=null ? (
<ArtistPortfolio masonry={true} columns={3} artistId={profileData["id"]} />
):null}
</Typography>
</Grid>
</Grid>
</CardContent>
</Card>
<Grid item xs={12} md={12}>
<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>
</CardContent>
</Card>
</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="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={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}>
<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.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}>
<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 item xs={12} md={3}>
<Rating
name="simple-controlled"
size="large"
value={rating}
disabled={!review || alreadyReviewed}
onChange={handleRatingChange}/>
</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>
</Grid>
</CardContent>
</Card>
)
:(
<>
<Typography variant="h6" component="h6" gutterBottom>
Loading...
</Typography>
<Box sx={{paddingTop:"20%"}}></Box>
<CircularProgress/>
</>
)}
</>
);
};
// Protected route, checking user authentication client-side.(CSR)
export default withPageAuthRequired(Profile);
export default withPageAuthRequired(RequestDetails);