123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354 |
- import {useEffect, useState} from "react";
- import {useDropzone} from "react-dropzone";
- import {sortableContainer, sortableElement} from "react-sortable-hoc";
- import {arrayMoveImmutable} from "array-move";
- import Box from "@mui/material/Box";
- import Typography from "@mui/material/Typography";
- import {
- Button,
- CircularProgress, Container,
- Grid, IconButton,
- InputAdornment,
- TextField
- } from "@mui/material";
- import {connect} from "react-redux";
- import {actionAllCategory} from "../../actions/ActionCategory";
- import {actionGoodUpsert} from "../../actions/ActionCreateGood";
- import Autocomplete from "@mui/material/Autocomplete";
- import {actionFullGoodFind, actionGoodCount} from "../../actions/ActionGoodFind";
- import {actionUploadFile} from "../../actions/ActionUploadFile";
- import {backURL} from "../../actions/PathDB";
- import {actionClearPromise} from "../../reducers/PromiseReducer";
- import {Link} from "react-router-dom";
- import CheckCircleOutlineIcon from '@mui/icons-material/CheckCircleOutline';
- import SearchIcon from "@material-ui/icons/Search";
- import imgNotFound from "../../img/catalog/imgNotFound.png";
- import {actionSearchRemove} from "../../reducers/SearchReducer";
- const GoodEdit = ({entity={images: [], categories: []}, onSave, onFileDrop, fileStatus,
- categoryState, actionRootCat, goodCount, goods, actionClear, result}) => {
- const [state, setState] = useState(entity)
- const {getRootProps, getInputProps, isDragActive} = useDropzone({accept: 'image/*', onDrop: acceptedFiles => {
- acceptedFiles.forEach(async file => {
- await onFileDrop(file)
- })
- }})
- const SortableItem = sortableElement(({value}) => {
- return (
- <Box key={value?._id} sx={{display: 'flex', justifyContent: 'center', borderRadius: 2, border: '1px solid #eaeaea', marginBottom: 2, width: 200, height: 200, padding: '5px', boxSizing: 'border-box'}}>
- <Box sx={{display: 'flex', justifyContent: 'center', minWidth: 0, overflow: 'hidden', position: 'relative'}}>
- {value?.url ?
- <>
- <img src={backURL+ '/' + value.url} style={{display: 'block', width: 'auto', height: '100%', objectFit: 'cover', objectPosition: 'center center'}} alt={value.name}/>
- </> :
- <Box sx={{ display: 'flex', justifyContent: 'center', alignItems: 'center'}}>
- <CircularProgress />
- </Box>
- }
- </Box>
- </Box>
- )
- });
- const SortableContainer = sortableContainer(({children}) => {
- return (
- <aside style={{display:'flex', justifyContent: 'space-between', flexWrap: 'wrap'}}>
- {children}
- </aside>
- )
- })
- const onSortEnd = ({oldIndex, newIndex}) => {
- setState(({images}) => ({
- ...state,
- images: arrayMoveImmutable(images, oldIndex, newIndex),
- }));
- }
- const handleClear = () => {
- setState(entity)
- }
- const handleOnSave = () => {
- let query = {...state}
- state.images?.length > 0 ? query.images = state.images.map(item => {return {'_id': item['_id']}}) : delete query.images
- state.categories?.length > 0 ? query.categories = state.categories.map(item => {return {'_id': item['_id'], 'name': item['name']}}) : delete query.categories
- onSave(query)
- goodCount()
- }
- const handleFullClear = () => {
- setState(entity)
- actionClear('goodUpsert')
- actionClear('uploadFile')
- }
- useEffect(() => {
- if(!categoryState) actionRootCat()
- if(!goods) goodCount()
- if(fileStatus?.status === 'RESOLVED'){
- state.images?.length > 0 ?
- setState({...state, images: [...state.images, fileStatus?.payload]}) :
- setState({...state, images: [fileStatus?.payload]})
- }
- },[categoryState, goods, fileStatus])
- return (
- <>
- {!result ?
- <>
- <Typography variant='h6' letterSpacing='2px' marginBottom='20px'>Total products: {goods?.payload || 0}</Typography>
- <Box style={{minHeight: "200px", border: '1px dashed #616161', borderRadius: '20px', padding: '20px'}} {...getRootProps()}>
- <input {...getInputProps()} />
- {isDragActive ?
- <Typography variant='body1' textAlign='center' color='#616161'>Drop the file here ...</Typography> :
- <Typography variant='body1' textAlign='center' color='#616161' marginBottom='20px'>Drag 'n' drop image files here, or click to select file</Typography>
- }
- <SortableContainer axis="xy" onSortEnd={onSortEnd}>
- {state.images?.length > 0 && state.images.map((value, index) => (
- <SortableItem key={`item-${value?._id || index}`} index={index} value={value} />
- ))}
- </SortableContainer>
- </Box>
- <Grid container justifyContent='space-between' marginTop='30px'>
- <Grid item xs={5.5}>
- <TextField fullWidth id="filled-basic" label="Title product" variant="standard" value={state?.name || ''} onChange={e => setState({...state, name: e.target.value})}/>
- </Grid>
- <Grid item xs={5.5}>
- {categoryState && categoryState?.payload && categoryState.payload?.length > 0 &&
- <>
- {state.categories?.length > 0 ?
- <Autocomplete
- multiple
- id="tags-standard"
- options={Object.values(categoryState.payload)}
- defaultValue={state.categories}
- onChange={(event, newValue) => {
- setState({...state, categories: [...newValue]})
- }}
- getOptionLabel={(option) => option?.name || 'no name'}
- key={option => option?.id}
- renderInput={(params) => (
- <TextField
- {...params}
- variant="standard"
- label="Select categories"
- placeholder="categories"
- />
- )}
- /> :
- <Autocomplete
- multiple
- id="tags-standard"
- options={Object.values(categoryState.payload)}
- onChange={(event, newValue) => {
- setState({...state, categories: [...newValue]})
- }}
- getOptionLabel={(option) => option?.name || 'no name'}
- key={option => option?.id}
- renderInput={(params) => (
- <TextField
- {...params}
- variant="standard"
- label="Select categories"
- placeholder="categories"
- />
- )}
- />
- }
- </>
- }
- </Grid>
- </Grid>
- <Grid container justifyContent='space-between' marginTop='30px'>
- <Grid item xs={5.5}>
- <TextField fullWidth
- id='Price'
- type='number'
- label='Price'
- variant='standard'
- value={state?.price || ''}
- onChange={e => setState({...state, price: parseFloat(e.target.value < 0 ? 0 : e.target.value)})}
- />
- </Grid>
- <Grid item xs={5.5}>
- <TextField fullWidth
- id='filled-basic'
- label='Description product'
- variant='standard'
- multiline
- value={state?.description || ''}
- onChange={e => setState({...state, description: e.target.value})}
- />
- </Grid>
- </Grid>
- <Grid container justifyContent='space-between' marginTop='30px'>
- <Grid item xs={5.5} display='flex' justifyContent='center'>
- <Button
- fullWidth
- onClick={handleClear}
- variant="outlined"
- color='warning'
- >
- Clear
- </Button>
- </Grid>
- <Grid item xs={5.5} display='flex' justifyContent='center'>
- <Button
- fullWidth
- variant="outlined"
- color='primary'
- onClick={handleOnSave}
- >
- Save
- </Button>
- </Grid>
- </Grid>
- </> :
- result?.payload?._id ?
- <>
- <Box display='flex' alignItems='center' flexDirection='column'>
- <Typography variant='h5' letterSpacing='2px' textAlign='center' color='#616161' marginBottom='20px'>Product successfully created!</Typography>
- <CheckCircleOutlineIcon sx={{marginBottom: '20px'}}/>
- <Link to={`/good/${result.payload._id}`} style={{color:'#616161', marginBottom:'20px'}}>
- <Typography variant='h5' letterSpacing='2px' textAlign='center' color='#616161'>View results</Typography>
- </Link>
- <Button variant='outlined' onClick={handleFullClear}>Add more</Button>
- </Box>
- </> :
- result?.error ?
- <Box display='flex' alignItems='center' flexDirection='column'>
- <Typography variant='h5' letterSpacing='2px' textAlign='center' color='#f00' marginBottom='20px'>Fatal error, try again!</Typography>
- <Button variant='outlined' onClick={handleFullClear}>Add more</Button>
- </Box>
- :
- <Box sx={{ display: 'flex' }}>
- <CircularProgress />
- </Box>
- }
- </>
- )
- }
- export const CGoodEdit = connect(state => ({fileStatus: state.promise['uploadFile'],
- categoryState: state.promise['allCategory'], goods: state.promise['goodCount'], result: state.promise['goodUpsert']}),
- {actionRootCat: actionAllCategory, onSave: actionGoodUpsert, goodCount: actionGoodCount,
- onFileDrop: actionUploadFile, actionClear: actionClearPromise})(GoodEdit)
- const ItemFound = ({item:{_id, name, price, images, description, categories}}) => {
- let [state, setState] = useState(false)
- return (
- !state ?
- <Button fullWidth sx={{display: 'flex', justifyContent:'flex-start'}} onClick={() => setState(true)}>
- <Box style={{display: 'flex', alignItems: 'center', marginBottom: '30px'}}>
- <Box width='60px' height='60px' borderRadius='10px' overflow='hidden' marginRight='60px'
- position='relative'>
- <img style={{
- position: 'absolute',
- top: '0',
- left: '0',
- width: '100%',
- height: '100%',
- objectFit: 'cover'
- }} src={images && Array.isArray(images) && images[0]?.url ? backURL + '/' + images[0].url : imgNotFound}
- alt={name}/>
- </Box>
- <Box sx={{
- display: 'flex',
- flexDirection: 'column',
- justifyContent: 'space-between',
- alignItems: 'flex-start'
- }}>
- <Typography
- color='#000'
- letterSpacing='1px'
- fontFamily='sarif'
- fontWeight='600'
- variant='h6'
- >
- {name || 'no name'}
- </Typography>
- <Typography
- letterSpacing='1px'
- variant='body1'
- fontWeight='300'
- color='#616161'
- margin='10px 0'
- sx={{textTransform: 'capitalize'}}
- >
- {description?.length > 60 ? 'Lorem ipsum dolor sit amet, consectetur adipisicing elit.' : description}
- </Typography>
- <Typography
- color='#000'
- letterSpacing='1px'
- variant='body1'
- fontWeight='600'
- >
- ${parseFloat(price).toFixed(2)}
- </Typography>
- </Box>
- </Box>
- </Button>
- :
- <Box sx={{marginBottom: '30px', border: '1px solid #616161', borderRadius: '10px', padding: '30px 20px'}}>
- <CGoodEdit entity={{_id, name, price, images, description, categories}}/>
- <Button variant='outlined' sx={{marginTop: '30px'}} fullWidth onClick={() => setState(false)}>Cansel</Button>
- </Box>
- )
- }
- const NotFound = () => {
- return (
- <Typography
- textAlign='center'
- color='#000'
- letterSpacing='1px'
- variant='body1'
- >
- No results found
- </Typography>
- )
- }
- const FindGoodEdit = ({searchResult, onSearch, onSearchRemove}) => {
- const [value, setValue] = useState('')
- const [click, setClick] = useState(false)
- return (
- <>
- <Container maxWidth="md">
- <Typography
- variant='h5'
- fontFamily='sarif'
- letterSpacing='3px'
- marginBottom='30px'
- marginTop='30px'
- textAlign='center'
- >
- WHICH ITEM TO EDIT?
- </Typography>
- <TextField
- color={'primary'}
- fullWidth
- variant="standard"
- value={value}
- placeholder="Start typing..."
- onChange={(event) => {setClick(false); setValue(event.target.value); onSearchRemove()}}
- InputProps={{
- sx: {padding: '10px', outline:'none', color: '#616161', fontWeight: '300', letterSpacing: '1px', marginBottom: '50px'},
- endAdornment: (
- <InputAdornment position="end">
- <IconButton onClick={() => {setClick(true); onSearchRemove(); onSearch(value)}}>
- <SearchIcon />
- </IconButton>
- </InputAdornment>
- )
- }}
- />
- {(value !== '' && click) && (searchResult?.searchResult ?
- Object.values(searchResult.searchResult).length > 0 ?
- Object.values(searchResult.searchResult).map(item => <ItemFound key={item?._id} item={item}/>) : <NotFound/> :
- <CircularProgress color="inherit"/>
- )}
- </Container>
- </>
- )
- }
- export const CFindGoodEdit = connect(state=>({searchResult: state.search}),
- {onSearch: actionFullGoodFind, onSearchRemove: actionSearchRemove})(FindGoodEdit)
|