ProfilePage.jsx 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280
  1. import Breadcrumb from "../components/Breadcrumbs";
  2. import {connect} from "react-redux";
  3. import {Avatar, Box, Button, Container, Grid, TextField, Typography, useMediaQuery} from "@mui/material";
  4. import Redirect from "react-router-dom/es/Redirect";
  5. import Tabs from "@mui/material/Tabs";
  6. import Tab from "@mui/material/Tab";
  7. import {createRef, useCallback, useEffect, useState} from "react";
  8. import PropTypes from "prop-types";
  9. import ManageAccountsIcon from '@mui/icons-material/ManageAccounts';
  10. import CancelIcon from '@mui/icons-material/Cancel';
  11. import SendAndArchiveIcon from '@mui/icons-material/SendAndArchive';
  12. import {actionAuthLogout} from "../reducers/AuthReducer";
  13. import {actionUserRemove} from "../reducers/UserReducer";
  14. import {backURL} from "../actions/PathDB";
  15. import {CMainOrders} from "./MyOrdersPage";
  16. import {CMainWishList} from "./WishListPage";
  17. import Dropzone, {useDropzone} from 'react-dropzone';
  18. import {
  19. actionFullUserUpdate,
  20. actionSetAvatar,
  21. actionSetLogin,
  22. actionSetNick, actionSetPassword,
  23. actionUploadFile
  24. } from "../actions/ActionUploadFile";
  25. import {actionFullUserFindOne} from "../actions/ActionUserFind";
  26. function TabPanel(props) {
  27. const { children, value, index, ...other } = props;
  28. return (
  29. <div
  30. role="tabpanel"
  31. hidden={value !== index}
  32. id={`vertical-tabpanel-${index}`}
  33. aria-labelledby={`vertical-tab-${index}`}
  34. style={{width: '100%'}}
  35. {...other}
  36. >
  37. {value === index && (
  38. <Box sx={{ p: 3}}>
  39. <Typography>{children}</Typography>
  40. </Box>
  41. )}
  42. </div>
  43. );
  44. }
  45. TabPanel.propTypes = {
  46. children: PropTypes.node,
  47. index: PropTypes.number.isRequired,
  48. value: PropTypes.number.isRequired,
  49. };
  50. function a11yProps(index) {
  51. return {
  52. id: `vertical-tab-${index}`,
  53. 'aria-controls': `vertical-tabpanel-${index}`,
  54. };
  55. }
  56. const ItemTabsAccountDefault = ({title, content}) => {
  57. const matches = useMediaQuery('(max-width:899px)')
  58. return(
  59. <Grid item xs={6} sm={4} marginBottom='20px'>
  60. <Typography
  61. color='#616161'
  62. fontWeight='300'
  63. marginBottom='5px'
  64. fontSize={matches ? '13px' : '16px'}
  65. >
  66. {title}
  67. </Typography>
  68. <Typography
  69. color='#000'
  70. fontWeight='400'
  71. fontSize={matches ? '16px' : '22px'}
  72. >
  73. {content}
  74. </Typography>
  75. </Grid>
  76. )
  77. }
  78. const MyDropzone = ({onLoad}) => {
  79. const [files, setFiles] = useState([]);
  80. const {getRootProps, getInputProps, isDragActive} = useDropzone({accept: 'image/*', onDrop: acceptedFiles => {
  81. setFiles(acceptedFiles.map(file => Object.assign(file, {
  82. preview: URL.createObjectURL(file)
  83. })));
  84. }})
  85. const thumbs = files.map(file => (
  86. <div key={file.name} style={{display: 'inline-flex', borderRadius: 2,border: '1px solid #eaeaea',marginBottom: 8, marginRight: 8, width: 500, height: 500, padding: 4, boxSizing: 'border-box'}}>
  87. <div style={{display: 'flex', minWidth: 0, overflow: 'hidden'}}>
  88. <img src={file.preview} style={{display: 'block', width: 'auto', height: '100%', objectFit: 'cover', objectPosition: 'center center'}} alt={'avatar'}/>
  89. </div>
  90. </div>
  91. ));
  92. useEffect(() => {
  93. files.forEach(file => URL.revokeObjectURL(file.preview));
  94. onLoad(files)
  95. }, [files]);
  96. return (
  97. <section style={{display: 'flex', flexDirection: 'column', justifyContent: 'space-between', alignItems: 'center', width:"100%", borderRadius: '20px', padding: '20px'}}>
  98. <div style={{width:"100%", height: "100%", border: '1px dashed #616161', borderRadius: '20px', padding: '20px'}} {...getRootProps()}>
  99. <input {...getInputProps()} />
  100. {isDragActive ?
  101. <Typography variant='body1' color='#616161'>Drop the file here ...</Typography> :
  102. <Typography variant='body1' color='#616161'>Drag 'n' drop image files here, or click to select file</Typography>
  103. }
  104. <aside>
  105. {thumbs}
  106. </aside>
  107. </div>
  108. </section>
  109. )
  110. }
  111. const FormUpload = ({user, setStatus, setLogin, setNick, setPassword, setImage}) => {
  112. const [loginValue, setLoginValue] = useState(user?.login || '')
  113. const [nickValue, setNickValue] = useState(user?.nick || '')
  114. const [passwordValue, setPasswordValue] = useState('')
  115. const [fileValue, setFileValue] = useState('')
  116. return (
  117. <Grid container spacing={2} justifyContent={'center'} textAlign='center' flexDirection='column'>
  118. <Grid item xs={12} display='flex' justifyContent='space-between' alignItems='center' marginBottom='30px'>
  119. <TextField sx={{color: '#000'}} label={'Login'} variant="outlined" placeholder={user?.login || ''} onChange={e => setLoginValue(e.target.value)}/>
  120. <TextField sx={{color: '#000'}} label={'Nick'} variant="outlined" placeholder={user?.nick || ''} onChange={e => setNickValue(e.target.value)}/>
  121. <TextField sx={{color: '#000'}} label={'Password'} type='password' variant="outlined" onChange={e => setPasswordValue(e.target.value)}/>
  122. </Grid>
  123. <Grid item xs={12} display='flex' justifyContent='space-between' alignItems='center' marginBottom='30px'>
  124. <MyDropzone onLoad={value => setFileValue(value)}/>
  125. </Grid>
  126. <Grid item xs={12} display='flex' justifyContent='center' alignItems='center'>
  127. <Button
  128. style={{ color: '#1976d2'}}
  129. fullWidth
  130. type='submit'
  131. onClick={() => {
  132. if (loginValue !== user?.login) {
  133. setLogin(loginValue)
  134. }
  135. if (nickValue !== user?.nick) {
  136. setNick(nickValue)
  137. }
  138. if (passwordValue){
  139. setPassword(passwordValue)
  140. }
  141. if (Array.isArray(fileValue) && fileValue[0]) {
  142. setImage(fileValue[0]);
  143. }
  144. setStatus(false)
  145. }}
  146. >
  147. <SendAndArchiveIcon style={{marginRight: '5px'}}/>
  148. Save
  149. </Button>
  150. <Button
  151. style={{ color: '#1976d2'}}
  152. fullWidth
  153. onClick={() => setStatus(false)}
  154. >
  155. <CancelIcon style={{marginRight: '5px'}}/>
  156. Cancel
  157. </Button>
  158. </Grid>
  159. </Grid>
  160. )
  161. }
  162. const CFormUpload = connect(null, {setLogin: actionSetLogin, setNick: actionSetNick, setPassword: actionSetPassword, setImage: actionSetAvatar, userUpdate: actionFullUserFindOne})(FormUpload)
  163. const AccountDetails = ({promise, user, time}) => {
  164. const [status, setStatus] = useState(false)
  165. return (
  166. !status ?
  167. <Grid container spacing={2} justifyContent={'space-between'} alignItems={'center'} textAlign={'center'}>
  168. <ItemTabsAccountDefault title={'Login'} content={user?.login}/>
  169. <ItemTabsAccountDefault title={'Nick'} content={user?.nick}/>
  170. <ItemTabsAccountDefault title={'Status account'} content={user?.acl[2] || user?.acl[1]}/>
  171. <ItemTabsAccountDefault title={'Account creation date'} content={time}/>
  172. <ItemTabsAccountDefault title={'Avatar'}
  173. content={
  174. (user?.avatar?.url ? <Avatar style={{margin: '0 auto'}} alt="User" src={backURL + '/' + user?.avatar?.url}/> : 'Not installed')
  175. }
  176. />
  177. <Grid item xs={12} md={4}>
  178. <Typography
  179. sx={{cursor: 'pointer'}}
  180. color={'#1976d2'}
  181. display='flex'
  182. justifyContent='center'
  183. alignItems='center'
  184. variant='h6'
  185. onClick={() => setStatus(true)}
  186. >
  187. <ManageAccountsIcon style={{marginRight: '10px'}}/>
  188. Edit data
  189. </Typography>
  190. </Grid>
  191. {!promise.setNewLogin?.payload && promise.setNewLogin?.status === 'RESOLVED' && <Typography width='100%' textAlign='center' color='red'>this login already exists</Typography>}
  192. </Grid> :
  193. <CFormUpload user={user} setStatus={value => setStatus(value)}/>
  194. )
  195. }
  196. const ProfilePage = ({user = {}, promise, authLogOut, userLogOut}) => {
  197. const matches = useMediaQuery('(max-width:899px)')
  198. const matches2 = useMediaQuery('(max-width:768px)')
  199. const [value, setValue] = useState(0)
  200. const handleChange = (event, newValue) => {
  201. setValue(newValue);
  202. };
  203. let formattedTime = 0;
  204. if (Object.keys(user).length !== 0) {
  205. let date = new Date(+user.createdAt);
  206. let year = date.getFullYear();
  207. let month = "0" + (date.getMonth()+1);
  208. let day = "0" + date.getDate();
  209. let hours = "0" + date.getHours();
  210. let minutes = "0" + date.getMinutes();
  211. let seconds = "0" + date.getSeconds();
  212. formattedTime = day.substr(-2) + '.' + month.substr(-2) + '.' + year +
  213. ' ' + hours.substr(-2) + ':' + minutes.substr(-2) + ':' + seconds.substr(-2);
  214. }
  215. return (
  216. Object.keys(user).length > 1 &&
  217. <>
  218. <Breadcrumb links={['Profile']}/>
  219. <main style={{backgroundColor: "#f3f3f3", padding: matches ? "20px 0" : "50px 0"}}>
  220. <Container maxWidth="lg">
  221. <Box>
  222. <Typography
  223. variant='h5'
  224. textAlign='center'
  225. fontFamily='sarif'
  226. marginBottom={matches ? '20px':'40px'}
  227. >
  228. LOGGED IN AS <strong>{user.login.toUpperCase()}</strong>
  229. </Typography>
  230. </Box>
  231. <Box
  232. sx={{ flexGrow: 1, bgcolor: '#fff', display: 'flex', height: '100%', alignItems: 'center'}}
  233. flexDirection={matches2 ? 'column': "row"}
  234. >
  235. <Tabs
  236. orientation={matches2 ? 'horizontal': "vertical"}
  237. variant="scrollable"
  238. value={value}
  239. onChange={handleChange}
  240. aria-label="Profile settings"
  241. sx={{ borderRight: 1, borderColor: 'divider', padding: '50px 0', height: '100%'}}
  242. >
  243. <Tab sx={{padding: '0 50px', textAlign: 'center'}} label={'ACCOUNT DETAILS'} {...a11yProps(0)} />
  244. <Tab sx={{padding: '0 50px', textAlign: 'center'}} label={'my orders'} {...a11yProps(1)} />
  245. <Tab sx={{padding: '0 50px', textAlign: 'center'}} label={'wish list'} {...a11yProps(2)} />
  246. <Button onClick={() => {authLogOut(); userLogOut()}}>Logout</Button>
  247. </Tabs>
  248. <TabPanel value={value} index={0}>
  249. <AccountDetails user={user} promise={promise} time={formattedTime}/>
  250. </TabPanel>
  251. <TabPanel value={value} index={1}>
  252. <CMainOrders itemsPerPage={5}/>
  253. </TabPanel>
  254. <TabPanel value={value} index={2}>
  255. <CMainWishList color={'#fff'}/>
  256. </TabPanel>
  257. </Box>
  258. </Container>
  259. </main>
  260. </>
  261. )
  262. }
  263. const CProfilePage = connect(state => ({user: state.user, promise: state.promise}), {authLogOut: actionAuthLogout, userLogOut: actionUserRemove})(ProfilePage)
  264. export default CProfilePage