Browse Source

done part with adding new contact

unknown 2 years ago
parent
commit
64f6f57062
30 changed files with 427 additions and 333 deletions
  1. 1 1
      .eslintcache
  2. 8 9
      src/App.tsx
  3. 23 1
      src/api-data/index.ts
  4. 85 0
      src/components/HomePage/LeftBar/AddContact/index.tsx
  5. 0 219
      src/components/HomePage/LeftBar/ContactsList/contacts.tsx
  6. 33 29
      src/components/HomePage/LeftBar/ContactsList/index.tsx
  7. 1 1
      src/components/HomePage/LeftBar/SmallMenuBar/index.tsx
  8. 2 1
      src/components/HomePage/LeftBar/index.tsx
  9. 9 20
      src/components/HomePage/RightBar/HeaderBar/Credentials/index.tsx
  10. 4 4
      src/components/HomePage/RightBar/index.tsx
  11. 17 0
      src/components/reusableComponents/AlertInfo/index.tsx
  12. 19 2
      src/components/reusableComponents/Loader/Loader.jsx
  13. 0 14
      src/components/reusableComponents/Modal/index.tsx
  14. 12 0
      src/helpers/index.ts
  15. 9 2
      src/index.tsx
  16. 13 7
      src/redux/authorization/reducer/index.ts
  17. 16 0
      src/redux/contacts/action/index.ts
  18. 31 0
      src/redux/contacts/operations/index.ts
  19. 24 0
      src/redux/contacts/reducer/index.ts
  20. 9 0
      src/redux/contacts/selector/index.ts
  21. 8 3
      src/redux/controlApp/action/index.ts
  22. 27 5
      src/redux/controlApp/reducer/index.ts
  23. 4 2
      src/redux/controlApp/selector/index.ts
  24. 2 0
      src/redux/rootReducer/index.ts
  25. 14 7
      src/typescript/redux/authorization/interfaces.ts
  26. 19 0
      src/typescript/redux/contacts/interfaces.ts
  27. 18 0
      src/typescript/redux/contacts/types.ts
  28. 13 3
      src/typescript/redux/controlApp/interfaces.ts
  29. 1 0
      src/typescript/redux/controlApp/types.ts
  30. 5 3
      src/typescript/redux/interfaces.ts

File diff suppressed because it is too large
+ 1 - 1
.eslintcache


+ 8 - 9
src/App.tsx

@@ -11,7 +11,7 @@ import { asyncLogout, asyncCurrentUser } from './redux/authorization/operations'
 import { setToken } from './api-data'
 import PrivateRoute from './components/reusableComponents/Routes/PrivateRoute';
 import PublicRoute from './components/reusableComponents/Routes/PublicRoute';
-import Loader from './components/reusableComponents/Loader/Loader';
+import {Load,CLoad} from './components/reusableComponents/Loader/Loader';
 
 const HomePage = lazy(
   () =>
@@ -48,9 +48,9 @@ function App() {
   }, [dispatch,token])
 
   return (
-    <div className={s.appWrapper}>
-      <MuiPickersUtilsProvider utils={DateFnsUtils}>
-      <Suspense fallback={<Loader />}>
+  <div className={s.appWrapper}>
+    <MuiPickersUtilsProvider utils={DateFnsUtils}>
+      <Suspense fallback={<Load/>}>
         <BrowserRouter>
           <Switch>
             <PrivateRoute exact path="/">
@@ -61,7 +61,7 @@ function App() {
               </PublicRoute>
           </Switch>
         </BrowserRouter>
-        <Loader/>
+        <CLoad/>
       </Suspense>
       <ToastContainer
         position="top-right"
@@ -74,10 +74,9 @@ function App() {
         draggable
         pauseOnHover
       />
-      </MuiPickersUtilsProvider>
-    </div>
-  );
-}
+    </MuiPickersUtilsProvider>
+  </div>
+)}
 
 
 export default App;

+ 23 - 1
src/api-data/index.ts

@@ -83,6 +83,26 @@ const currentUser = async <T>(): Promise<T | undefined> => {
   } 
 };
 
+const addContact = async <T>(number:string): Promise<T | undefined> => {
+  try {
+    const { data : {data} } = await axios.post('/contacts', { number });
+    success('CONTACT ADDED');
+    return data
+  } catch (e) {
+    error('BAD REQUEST');
+  }
+};
+
+const getContacts = async <T>(): Promise<T | undefined> => {
+  try {
+    const { data : {data} } = await axios.get('/contacts');
+    success('CONTACTS LOADED');
+    return data
+  } catch (e) {
+    error('BAD REQUEST');
+  }
+};
+
 
 
 export {
@@ -91,5 +111,7 @@ export {
   loginUser,
   updateCredentials,
   updateUserAvatar,
-  currentUser
+  currentUser,
+  addContact,
+  getContacts
 };

+ 85 - 0
src/components/HomePage/LeftBar/AddContact/index.tsx

@@ -0,0 +1,85 @@
+import { makeStyles, Button, TextField, Typography } from '@material-ui/core'
+import { useState } from 'react';
+import { useDispatch } from 'react-redux';
+import { asyncAddContact } from '../../../../redux/contacts/operations'
+
+const useStyles = makeStyles({
+  container: {
+    display: 'flex',
+    alignItems: 'center',
+    alignContent:'center',
+    flexDirection: 'column',
+    width: 350,
+    margin: '0 auto',
+    paddingTop: 64,
+    paddingBottom: 24,
+  },
+  title: {
+    marginBottom: 20,
+    textAlign: 'center',
+  },
+  buttonNext: {
+    marginTop: 20,
+    height: 50,
+    color: '#f8f8f8',
+    backgroundColor:'#1d74c5',
+  },
+  textField: {
+    marginBottom:20
+  }
+})
+
+const AddContact = () => {
+  const classes = useStyles()
+  const dispatch = useDispatch()
+  const [number, setNumber] = useState<string>('')
+
+  const handleTextField = (e: React.ChangeEvent<HTMLInputElement>) => {
+    const value = e.target.value.split(' ').join('').trim()
+    setNumber(value)
+  }
+  const handleAddContact = async() => {
+    await dispatch(asyncAddContact(number))
+    setNumber('')
+  }
+
+  const isValidNumber = () => {
+    if (number.length < 13 || number.length > 13) return false
+    if(number.slice(0,1) === '+') return true
+    return true
+  }
+
+ 
+  return (
+    <div className={classes.container} >
+      <Typography
+        className={classes.title}
+        variant="h5"
+        color="initial">
+        Add new contact
+      </Typography>      
+      <TextField
+        id="number"
+        name='NUMBER'
+        label="Write down a number,+ is requirement"
+        value={number}
+        fullWidth
+        variant='outlined'
+        onChange={handleTextField}
+        className={classes.textField}
+        required
+        />
+      {isValidNumber() &&
+        <Button
+        onClick={handleAddContact}
+        className={classes.buttonNext}
+        color='primary'
+        variant="contained"
+        fullWidth
+        > ADD
+      </Button>}
+    </div>
+  );
+};
+
+export default AddContact;

+ 0 - 219
src/components/HomePage/LeftBar/ContactsList/contacts.tsx

@@ -1,219 +0,0 @@
-export  const contacts = [
-    {
-        avatarUrl: 'https://www.seekpng.com/png/full/114-1149126_apple-clipart-black-and-white-image-small-clip.png',
-        name: 'Grigore',
-        lastName:'Helena',
-        message: 'Hello'
-    },
-    {
-        avatarUrl: 'https://www.seekpng.com/png/full/114-1149126_apple-clipart-black-and-white-image-small-clip.png',
-        name: 'Grigfffffffffffffffvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvfffffffffffffdddddddddfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff',
-        lastName:'Weddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddrr',
-        message: 'dddd'
-    },
-    {
-        avatarUrl: '',
-        name: 'Helena',
-        lastName:'Velikolug',
-        message: 'I am zaraza'
-    },
-    {
-        avatarUrl: 'https://www.seekpng.com/png/full/114-1149126_apple-clipart-black-and-white-image-small-clip.png',
-        name: 'Grigffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff',
-        lastName:'Wedrr',
-        message: 'ddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd'
-    },
-    {
-        avatarUrl: '',
-        name: 'Grigffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff',
-        lastName:'Wedrr',
-        message: 'ddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd'
-    },
-    {
-        avatarUrl: 'https://www.seekpng.com/png/full/114-1149126_apple-clipart-black-and-white-image-small-clip.png',
-        name: 'Grigffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff',
-        lastName:'Wedrr',
-        message: 'ddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd'
-    },
-    {
-        avatarUrl: '',
-        name: 'Grigffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff',
-        lastName:'Wedrr',
-        message: 'ddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd'
-    },
-    {
-        avatarUrl: '',
-        name: 'hrigffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff',
-        lastName:'uedrr',
-        message: 'ddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd'
-    },
-    {
-        avatarUrl: '',
-        name: 'Grigffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff',
-        lastName:'kjedrr',
-        message: 'ddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd'
-    },
-    {
-        avatarUrl: 'https://www.seekpng.com/png/full/114-1149126_apple-clipart-black-and-white-image-small-clip.png',
-        name: 'Grigffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff',
-        lastName:'Wedrr',
-        message: 'ddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd'
-    },
-    {
-        avatarUrl: 'https://www.seekpng.com/png/full/114-1149126_apple-clipart-black-and-white-image-small-clip.png',
-        name: 'Grigffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff',
-        lastName:'Wedrr',
-        message: 'ddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd'
-    },
-    {
-        avatarUrl: 'https://www.seekpng.com/png/full/114-1149126_apple-clipart-black-and-white-image-small-clip.png',
-        name: 'Grigffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff',
-        lastName:'Wedrr',
-        message: 'ddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd'
-    },
-    {
-        avatarUrl: 'https://www.seekpng.com/png/full/114-1149126_apple-clipart-black-and-white-image-small-clip.png',
-        name: 'Grigffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff',
-        lastName:'Wedrr',
-        message: 'ddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd'
-    },
-    {
-        avatarUrl: 'https://www.seekpng.com/png/full/114-1149126_apple-clipart-black-and-white-image-small-clip.png',
-        name: 'Grigffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff',
-        lastName:'Wedrr',
-        message: 'ddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd'
-    },
-    {
-        avatarUrl: 'https://www.seekpng.com/png/full/114-1149126_apple-clipart-black-and-white-image-small-clip.png',
-        name: 'Grigffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff',
-        lastName:'Wedrr',
-        message: 'ddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd'
-    },
-    {
-        avatarUrl: 'https://www.seekpng.com/png/full/114-1149126_apple-clipart-black-and-white-image-small-clip.png',
-        name: 'Grigffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff',
-        lastName:'Wedrr',
-        message: 'ddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd'
-    },
-    {
-        avatarUrl: 'https://www.seekpng.com/png/full/114-1149126_apple-clipart-black-and-white-image-small-clip.png',
-        name: 'Grigffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff',
-        lastName:'Wedrr',
-        message: 'ddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd'
-    },
-    {
-        avatarUrl: 'https://www.seekpng.com/png/full/114-1149126_apple-clipart-black-and-white-image-small-clip.png',
-        name: 'Grigffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff',
-        lastName:'Wedrr',
-        message: 'ddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd'
-    },
-    {
-        avatarUrl: 'https://www.seekpng.com/png/full/114-1149126_apple-clipart-black-and-white-image-small-clip.png',
-        name: 'Grigffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff',
-        lastName:'Wedrr',
-        message: 'ddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd'
-    },
-    {
-        avatarUrl: 'https://www.seekpng.com/png/full/114-1149126_apple-clipart-black-and-white-image-small-clip.png',
-        name: 'Grigffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff',
-        lastName:'Wedrr',
-        message: 'ddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd'
-    },
-    {
-        avatarUrl: 'https://www.seekpng.com/png/full/114-1149126_apple-clipart-black-and-white-image-small-clip.png',
-        name: 'Grigffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff',
-        lastName:'Wedrr',
-        message: 'ddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd'
-    },
-    {
-        avatarUrl: 'https://www.seekpng.com/png/full/114-1149126_apple-clipart-black-and-white-image-small-clip.png',
-        name: 'Grigffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff',
-        lastName:'Wedrr',
-        message: 'ddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd'
-    },
-    {
-        avatarUrl: 'https://www.seekpng.com/png/full/114-1149126_apple-clipart-black-and-white-image-small-clip.png',
-        name: 'Grigffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff',
-        lastName:'Wedrr',
-        message: 'ddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd'
-    },
-    {
-        avatarUrl: 'https://www.seekpng.com/png/full/114-1149126_apple-clipart-black-and-white-image-small-clip.png',
-        name: 'Grigffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff',
-        lastName:'Wedrr',
-        message: 'ddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd'
-    },
-    {
-        avatarUrl: 'https://www.seekpng.com/png/full/114-1149126_apple-clipart-black-and-white-image-small-clip.png',
-        name: 'Grigffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff',
-        lastName:'Wedrr',
-        message: 'ddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd'
-    },
-    {
-        avatarUrl: 'https://www.seekpng.com/png/full/114-1149126_apple-clipart-black-and-white-image-small-clip.png',
-        name: 'Grigffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff',
-        lastName:'Wedrr',
-        message: 'ddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd'
-    },
-    {
-        avatarUrl: 'https://www.seekpng.com/png/full/114-1149126_apple-clipart-black-and-white-image-small-clip.png',
-        name: 'Grigffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff',
-        lastName:'Wedrr',
-        message: 'ddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd'
-    },
-    {
-        avatarUrl: 'https://www.seekpng.com/png/full/114-1149126_apple-clipart-black-and-white-image-small-clip.png',
-        name: 'Grigffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff',
-        lastName:'Wedrr',
-        message: 'ddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd'
-    },
-    {
-        avatarUrl: 'https://www.seekpng.com/png/full/114-1149126_apple-clipart-black-and-white-image-small-clip.png',
-        name: 'Grigffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff',
-        lastName:'Wedrr',
-        message: 'ddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd'
-    },
-    {
-        avatarUrl: 'https://www.seekpng.com/png/full/114-1149126_apple-clipart-black-and-white-image-small-clip.png',
-        name: 'Grigffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff',
-        lastName:'Wedrr',
-        message: 'ddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd'
-    },
-    {
-        avatarUrl: 'https://www.seekpng.com/png/full/114-1149126_apple-clipart-black-and-white-image-small-clip.png',
-        name: 'Grigffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff',
-        lastName:'Wedrr',
-        message: 'ddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd'
-    },
-    {
-        avatarUrl: 'https://www.seekpng.com/png/full/114-1149126_apple-clipart-black-and-white-image-small-clip.png',
-        name: 'Grigffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff',
-        lastName:'Wedrr',
-        message: 'ddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd'
-    },
-    {
-        avatarUrl: 'https://www.seekpng.com/png/full/114-1149126_apple-clipart-black-and-white-image-small-clip.png',
-        name: 'Grigffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff',
-        lastName:'Wedrr',
-        message: 'ddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd'
-    },
-    {
-        avatarUrl: 'https://www.seekpng.com/png/full/114-1149126_apple-clipart-black-and-white-image-small-clip.png',
-        name: 'Grigffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff',
-        lastName:'Wedrr',
-        message: 'ddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd'
-    },
-    {
-        avatarUrl: 'https://www.seekpng.com/png/full/114-1149126_apple-clipart-black-and-white-image-small-clip.png',
-        name: 'Grigffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff',
-        lastName:'Wedrr',
-        message: 'ddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd'
-    },
-    {
-        avatarUrl: 'https://www.seekpng.com/png/full/114-1149126_apple-clipart-black-and-white-image-small-clip.png',
-        name: 'Grigffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff',
-        lastName:'Wedrr',
-        message: 'ddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd'
-    },    
-]
-

+ 33 - 29
src/components/HomePage/LeftBar/ContactsList/index.tsx

@@ -4,10 +4,16 @@ import Avatar from '@mui/material/Avatar';
 import ListItemText from '@mui/material/ListItemText';
 import ListItemIcon from '@mui/material/ListItemIcon';
 import { makeStyles } from '@material-ui/core'
-import { useState } from 'react';
-import  shortid  from 'shortid';
+import { useState,useEffect } from 'react';
+import shortid from 'shortid';
+import { useSelector,useDispatch } from 'react-redux';
 
-import { contacts } from './contacts'
+import AlertInfo from '../../../reusableComponents/AlertInfo'
+import { getState } from '../../../../redux/contacts/selector'
+import { asyncGetContacts } from '../../../../redux/contacts/operations'
+import { firstLetter, slicedWord, timeStamp } from '../../../../helpers'
+import { actionStartChat } from '../../../../redux/controlApp/action'
+import { TContact } from '../../../../typescript/redux/contacts/types'
 
 const useStyles = makeStyles({
   list: {
@@ -37,47 +43,45 @@ const useStyles = makeStyles({
   },
 })
 
-const  ChatsList = () => {
-  const [selectedIndex, setSelectedIndex] = useState<number>(1);
-
+const  ContactsList = () => {
   const classes = useStyles()
-
-  const handleListItemClick = (e: React.SyntheticEvent<Element, Event>, i: number) => {
-    console.log(i,'index','selected chat in chat bar',e)
+  const dispatch = useDispatch()
+  const { total, contacts } = useSelector(getState)
+  const [selectedIndex, setSelectedIndex] = useState<number>(1);  
+  const handleListItemClick = async (i:number, companion:TContact) => {
     setSelectedIndex(i);
+    await dispatch(actionStartChat(companion))
   }
 
-  const data = new Date(2022, 3, 8).toLocaleString("en-US", {
-    year:'numeric',
-    month: 'short',
-    day: 'numeric'
-  });
-
-  const firstLetter = (word: string) => word.slice(0, 1).toUpperCase()
-  const slicedWord = (word: string, max: number, from: number = 0) =>
-    word.length < max ? word.slice(from) : word.slice(from, max)
+  useEffect(() => {
+    dispatch(asyncGetContacts())
+  }, [dispatch])
 
-  return (
+  
+  return total !== '0' ? (
     <List
-        className={classes.list} component="nav"
-        aria-label="main mailbox folders">
-         {contacts && contacts.map(({name,lastName,avatarUrl}: any,i:number) =>
-          <ListItemButton
+      className={classes.list} component="nav"
+      aria-label="main mailbox folders">
+      {contacts.map((contact, i: number) => {
+          const { name, lastName, avatarUrl, updatedAt } = contact
+         return  (
+        <ListItemButton
           key={shortid.generate()}
           selected={selectedIndex === i}
-          onClick={(e) => handleListItemClick(e, i)}
+          onClick={() => handleListItemClick(i, contact)}
           >
           <ListItemIcon className={classes.listItem_iconAvatar}>
-            <Avatar alt={name} src={avatarUrl?avatarUrl:undefined}
+            <Avatar alt={name} src={avatarUrl?`http://localhost:3000/${avatarUrl}`:undefined}
                  sx={{ background: '#f0c712', width: 54, height: 54 }}>
                  {!avatarUrl&&`${firstLetter(name)}${firstLetter(lastName)}`}
               </Avatar>
           </ListItemIcon> 
           <ListItemText primary={`${firstLetter(name)}${slicedWord(name, 15, 1)}
-                ${firstLetter(lastName)}${slicedWord(lastName, 15, 1)}`} secondary={`last seen ${data}`} />        
-        </ListItemButton>)}
+               ${firstLetter(lastName)}${slicedWord(lastName, 15, 1)}`}
+               secondary={`last seen ${timeStamp(updatedAt)}`} />
+        </ListItemButton>)})}
       </List>
-  );
+  ):<AlertInfo name='You do not have any contact yet!' />;
 }
 
-export default ChatsList
+export default ContactsList

+ 1 - 1
src/components/HomePage/LeftBar/SmallMenuBar/index.tsx

@@ -115,7 +115,7 @@ const  SmallMenuBar = ({handleSelectedMenu,setIsMenuSm}:ISmallMenuBar) => {
           handleSelectedMenu(14)
         }}>
             <PersonAddAltIcon/>
-            Add to contacts
+            New Contact
          </MenuItem>        
        </StyledMenu>    
     </div>

+ 2 - 1
src/components/HomePage/LeftBar/index.tsx

@@ -8,6 +8,7 @@ import SearchBar from './SearchBar'
 import ChatsList from './ChatsList'
 import MenuBar from './MenuBar'
 import ContactsList from './ContactsList'
+import AddContact from './AddContact'
 import NotDone from '../../reusableComponents/NotDone'
 
 const LeftBar = () => {
@@ -83,7 +84,7 @@ const LeftBar = () => {
         {iMenu === 11 && <NotDone name='New Channel' />}
         {iMenu === 12 && <NotDone name='New Group' />}
         {iMenu === 13 && <ContactsList />}
-        {iMenu === 14 && <NotDone name='Add new contact' />}
+        {iMenu === 14 && <AddContact/>}
     </Grid>
     )
 }

+ 9 - 20
src/components/HomePage/RightBar/HeaderBar/Credentials/index.tsx

@@ -2,40 +2,29 @@ import ListItemButton from '@mui/material/ListItemButton';
 import Avatar from '@mui/material/Avatar';
 import ListItemText from '@mui/material/ListItemText';
 import ListItemIcon from '@mui/material/ListItemIcon';
-import { useDispatch } from 'react-redux';
+import { useDispatch,useSelector } from 'react-redux';
 
 import { actionIsOpen } from '../../../../../redux/controlApp/action'
-const contact = [{
-  avatarUrl: 'https://www.seekpng.com/png/full/114-1149126_apple-clipart-black-and-white-image-small-clip.png',
-  name: 'Grigore',
-  lastName:'Helena',
-  message: 'Hello'
-}]
+import { getCompanion } from '../../../../../redux/controlApp/selector'
+import { firstLetter,slicedWord,timeStamp } from '../../../../../helpers'
 
 const Credentials = () => {
   const dispatch = useDispatch()
-  const data = new Date(2022, 3, 8).toLocaleString("en-US", {
-    year:'numeric',
-    month: 'short',
-    day: 'numeric'
-  });
-
-  const firstLetter = (word: string) => word.slice(0, 1).toUpperCase()
-  const slicedWord = (word: string, max: number, from: number = 0) =>
-    word.length < max ? word.slice(from) : word.slice(from, max)
-  const {avatarUrl,name,lastName }:any = contact[0]
+  const companion = useSelector(getCompanion)
+  const { name,lastName,avatarUrl,updatedAt } = companion
   return (
     <ListItemButton onClick={() => dispatch(actionIsOpen('credentials'))}>
       <ListItemIcon >
-        <Avatar alt={name} src={avatarUrl?avatarUrl:undefined}
+        <Avatar alt={name} src={avatarUrl?`http://localhost:3000/${avatarUrl}`:undefined}
               sx={{ background: '#f0c712', width: 44, height: 44 }}>
               {!avatarUrl&&`${firstLetter(name)}${firstLetter(lastName)}`}
           </Avatar>
       </ListItemIcon> 
       <ListItemText primary={`${firstLetter(name)}${slicedWord(name, 15, 1)}
-            ${firstLetter(lastName)}${slicedWord(lastName, 15, 1)}`} secondary={data} />        
+        ${firstLetter(lastName)}${slicedWord(lastName, 15, 1)}`}
+        secondary={timeStamp(updatedAt)} />
     </ListItemButton>
-  );
+  )
 }
 
 export default Credentials

+ 4 - 4
src/components/HomePage/RightBar/index.tsx

@@ -3,7 +3,7 @@ import { makeStyles } from '@material-ui/core'
 import HeaderBar from './HeaderBar'
 import ChatBar from './ChatBar'
 import { useSelector } from 'react-redux'
-import { getIsOpen } from '../../../redux/controlApp/selector'
+import { getState } from '../../../redux/controlApp/selector'
 
 const useStyles = makeStyles({
   container: {
@@ -27,8 +27,8 @@ const useStyles = makeStyles({
 
 const RightBar = () => {
   const classes = useStyles()
-  const isOpen = useSelector(getIsOpen)
-    return (
+  const {isOpen,companion:{number}} = useSelector(getState)
+    return number?(
       <Grid item lg={9} className={classes.container}>
         <HeaderBar />
         <div className={classes.chat}>
@@ -36,7 +36,7 @@ const RightBar = () => {
           {isOpen&&isOpen !== 'menu'&&<div className={classes.moveChat}></div>}
         </div>
       </Grid>
-    )
+    ):<Grid item lg={9} className={classes.container}/>
 }
 
 export default RightBar

+ 17 - 0
src/components/reusableComponents/AlertInfo/index.tsx

@@ -0,0 +1,17 @@
+import Alert from '@mui/material/Alert';
+import AlertTitle from '@mui/material/AlertTitle';
+import Stack from '@mui/material/Stack';
+
+
+const AlertInfo = ({ name }: { name: string }) => {
+return (
+    <Stack sx={{ width: '100%' }} spacing={2}>
+      <Alert severity="info">
+        <AlertTitle>Warning</AlertTitle>
+        <strong>{name}</strong>
+      </Alert>
+    </Stack>
+  );
+}
+
+export default AlertInfo

+ 19 - 2
src/components/reusableComponents/Loader/Loader.jsx

@@ -5,7 +5,7 @@ import { useSelector } from 'react-redux';
 import {getLoad} from '../../../redux/loading/selector'
 import s from './Loader.module.css';
 
-const Load = () => {
+const CLoad = () => {
   const isLoading = useSelector(getLoad)
   return isLoading?(
     <Loader
@@ -18,4 +18,21 @@ const Load = () => {
     />
   ):null
 };
-export default Load;
+
+const Load = () => {
+  return (
+    <Loader
+      className={s.loader}
+      type="Puff"
+      color="#0ca0f5"
+      height={100}
+      width={100}
+      timeout={300000}
+    />
+  )
+};
+
+export {CLoad,Load};
+
+
+

+ 0 - 14
src/components/reusableComponents/Modal/index.tsx

@@ -1,17 +1,3 @@
-import Divider from '@mui/material/Divider';
-import Paper from '@mui/material/Paper';
-import MenuList from '@mui/material/MenuList';
-import MenuItem from '@mui/material/MenuItem';
-import ListItemText from '@mui/material/ListItemText';
-import ListItemIcon from '@mui/material/ListItemIcon';
-import BookmarkBorderIcon from '@mui/icons-material/BookmarkBorder';
-import ArchiveIcon from '@mui/icons-material/Archive';
-import PermContactCalendarIcon from '@mui/icons-material/PermContactCalendar';
-import SettingsIcon from '@mui/icons-material/Settings';
-import Brightness3Icon from '@mui/icons-material/Brightness3';
-import AnimationIcon from '@mui/icons-material/Animation';
-import HelpOutlineIcon from '@mui/icons-material/HelpOutline';
-import BugReportIcon from '@mui/icons-material/BugReport';
 import { makeStyles } from '@material-ui/core'
 import React from 'react';
 

+ 12 - 0
src/helpers/index.ts

@@ -0,0 +1,12 @@
+const firstLetter = (word: string) => word.slice(0, 1).toUpperCase()
+
+const slicedWord = (word: string, max: number, from: number = 0) =>
+    word.length < max ? word.slice(from) : word.slice(from, max)
+    
+const timeStamp = (updatedAt: string) => new Date(updatedAt).toLocaleString("en-US", {
+    year:'numeric',
+    month: 'short',
+    day: 'numeric'
+})
+
+export { firstLetter,slicedWord,timeStamp }

+ 9 - 2
src/index.tsx

@@ -7,13 +7,20 @@ import 'react-toastify/dist/ReactToastify.css';
 import 'modern-normalize/modern-normalize.css';
 import './index.css';
 import App from './App';
+import { Load } from './components/reusableComponents/Loader/Loader'
 import { store, persistor } from './redux/store';
+import { QueryClient, QueryClientProvider } from 'react-query';
+
+const queryClient = new QueryClient();
+
 
 ReactDOM.render(
   <React.StrictMode>
-    <PersistGate loading={null} persistor={persistor}>
+    <PersistGate loading={<Load/>} persistor={persistor}>
       <Provider store={store}>
-        <App />
+        <QueryClientProvider client={queryClient}>
+          <App />
+         </QueryClientProvider>
       </Provider>
     </PersistGate>
   </React.StrictMode>,

+ 13 - 7
src/redux/authorization/reducer/index.ts

@@ -10,13 +10,19 @@ import {
 } from '../action';
 
 const initialState:IAuthorizationState = {
-  token: '',
-  number: '',
-  name: '',
-  lastName: '',
-  country:'',
-  avatarUrl: '',
-};
+  name: "",
+  lastName: "",
+  avatarUrl: "",
+  color: "",
+  code: "",
+  _id: "",
+  token: "",
+  number: "" ,
+  country: "",
+  createdAt: "",
+  updatedAt: "",
+  __v: 0,
+}
 
 const reducerAuthorization = createReducer(initialState, {
   [actionLogInSuccess.type]: (state, { payload:token }: { payload: string }) => {

+ 16 - 0
src/redux/contacts/action/index.ts

@@ -0,0 +1,16 @@
+import { createAction } from '@reduxjs/toolkit';
+import { IContactsState } from '../../../typescript/redux/contacts/interfaces'
+
+const actionGetContactsSuccess = createAction('getContacts/success', (value:IContactsState) => ({
+  payload: value,
+}));
+
+const actionGetContactsReject = createAction('getContacts/reject', () => ({
+  payload: null,
+}));
+
+
+export {
+  actionGetContactsSuccess,
+  actionGetContactsReject
+};

+ 31 - 0
src/redux/contacts/operations/index.ts

@@ -0,0 +1,31 @@
+import { actionIsLoading } from '../../loading/action';
+import {
+  actionGetContactsSuccess,
+  actionGetContactsReject
+} from '../action';
+import { addContact,getContacts} from '../../../api-data';
+import { IContactsState } from '../../../typescript/redux/contacts/interfaces'
+
+const asyncGetContacts = () => async (dispatch:any) => {
+  try {
+    dispatch(actionIsLoading(true));
+    const data = await getContacts<IContactsState>()
+    data&&dispatch(actionGetContactsSuccess(data))
+  } catch (e) {
+    dispatch(actionGetContactsReject())
+  } finally {
+    dispatch(actionIsLoading(false));
+  }
+};
+
+const asyncAddContact = (number:string) => async (dispatch:any) => {
+  try {
+    dispatch(actionIsLoading(true));
+    await addContact(number)
+  } finally {
+    dispatch(actionIsLoading(false));
+  }
+};
+
+
+export { asyncAddContact,asyncGetContacts };

+ 24 - 0
src/redux/contacts/reducer/index.ts

@@ -0,0 +1,24 @@
+import { createReducer } from '@reduxjs/toolkit';
+import { IContactsState,IContactsPayload} from '../../../typescript/redux/contacts/interfaces';
+import {
+  actionGetContactsSuccess,
+  actionGetContactsReject
+} from '../action';
+
+const initialState:IContactsState = {
+  total: "0",
+  limit: "0",
+  page: "0",
+  contacts: []
+}
+
+const reducerContacts = createReducer(initialState, {
+  [actionGetContactsSuccess.type]: (_state,{ payload}:IContactsPayload) => {
+    return payload;
+  },
+  [actionGetContactsReject.type]: (state, _) => {
+    return state;
+  },
+});
+
+export default reducerContacts;

+ 9 - 0
src/redux/contacts/selector/index.ts

@@ -0,0 +1,9 @@
+import {IState} from '../../../typescript/redux/interfaces'
+
+const getTotal = (state: IState) => state.contacts.total;
+const getLimit = (state:IState) => state.contacts.limit;
+const getPage = (state: IState) => state.contacts.page;
+const getContacts = (state: IState) => state.contacts.contacts;
+const getState= (state:IState) => state.contacts;
+
+export { getTotal,getLimit,getPage,getContacts,getState };

+ 8 - 3
src/redux/controlApp/action/index.ts

@@ -1,8 +1,13 @@
 import { createAction } from '@reduxjs/toolkit';
-import { TState }  from '../../../typescript/redux/controlApp/interfaces'
+import { TIsOpen } from '../../../typescript/redux/controlApp/types'
+import { TContact } from '../../../typescript/redux/contacts/types'
 
-const actionIsOpen= createAction('control/isOpen', (value:TState) => ({
+const actionIsOpen= createAction('controlApp/isOpen', (value:TIsOpen) => ({
   payload: value,
 }));
 
-export { actionIsOpen };
+const actionStartChat= createAction('controlApp/startChat', (value:TContact) => ({
+  payload: value,
+}));
+
+export { actionIsOpen,actionStartChat };

+ 27 - 5
src/redux/controlApp/reducer/index.ts

@@ -1,11 +1,33 @@
 import { createReducer } from '@reduxjs/toolkit';
-import { actionIsOpen } from '../action';
-import { IPayload } from '../../../typescript/redux/controlApp/interfaces'
+import { actionIsOpen,actionStartChat } from '../action';
+import { IControlAppState,IPayloadIsOpen,IPayloadCompanion } from '../../../typescript/redux/controlApp/interfaces'
 
-const reducerControlApp = createReducer('', {
-  [actionIsOpen.type]: (_, { payload }:IPayload) => {
-    return payload
+const initialState:IControlAppState = {
+  isOpen: "",
+  companion: {
+  _id: '',
+  __v: 0,
+  avatarUrl: '',
+  color: '',
+  country: '',
+  createdAt: '',
+  lastName: '',
+  name: '',
+  number: '',
+  owner: {
+    token: '',
   },
+  updatedAt: '',
+  },
+}
+
+const reducerControlApp = createReducer(initialState, {
+  [actionIsOpen.type]: (state, { payload:isOpen }:IPayloadIsOpen) => {
+    return {...state,isOpen}
+  },
+  [actionStartChat.type]: (state, { payload:companion }:IPayloadCompanion) => {
+    return {...state,companion}
+  },  
 });
 
 export default reducerControlApp;

+ 4 - 2
src/redux/controlApp/selector/index.ts

@@ -1,5 +1,7 @@
 import { IState } from '../../../typescript/redux/interfaces'
 
-const getIsOpen = (state:IState) => state.controlApp;
+const getIsOpen = (state: IState) => state.controlApp.isOpen;
+const getCompanion = (state: IState) => state.controlApp.companion;
+const getState = (state:IState) => state.controlApp;
 
-export { getIsOpen };
+export { getIsOpen,getCompanion,getState };

+ 2 - 0
src/redux/rootReducer/index.ts

@@ -2,6 +2,7 @@ import { combineReducers } from '@reduxjs/toolkit';
 import { persistReducer } from 'redux-persist';
 import storage from 'redux-persist/lib/storage';
 
+import reducerContacts from '../contacts/reducer'
 import reducerControlApp from '../controlApp/reducer'
 import reducerLoading from '../loading/reducer';
 import reducerAuthorization from '../authorization/reducer';
@@ -13,6 +14,7 @@ const authorizationPersistConfig = {
 
 export const rootReducer = combineReducers({
   isLoading: reducerLoading,
+  contacts:reducerContacts,
   controlApp: reducerControlApp,
   authorization: persistReducer(
     authorizationPersistConfig,

+ 14 - 7
src/typescript/redux/authorization/interfaces.ts

@@ -1,12 +1,19 @@
 
 export interface IAuthorizationState  {
-        token: string,
-        number: string ,
-        name: string | null,
-        lastName: string | null,
-        country: string,
-        avatarUrl: string | null
-  }
+  name?: string | null,
+  lastName?: string | null,
+  avatarUrl?: string | null,
+  color?: string | null,
+  code?: string | null,
+  _id?: string | null,
+  token?: string | null,
+  number?: string | null ,
+  country?: string | null,
+  createdAt?: string | null,
+  updatedAt?: string | null,
+  __v?: number | null,
+}
+
 
 export interface IAuthorizationPayload  {
   payload: IAuthorizationState

+ 19 - 0
src/typescript/redux/contacts/interfaces.ts

@@ -0,0 +1,19 @@
+import { TContacts } from './types'
+
+export interface IContactsState  {
+  total: string,
+  limit: string,
+  page: string,
+  contacts: TContacts
+}
+
+export interface IContactsPayload {
+  payload: {
+  total: string,
+  limit: string,
+  page: string,
+  contacts: TContacts
+  },
+}
+
+

+ 18 - 0
src/typescript/redux/contacts/types.ts

@@ -0,0 +1,18 @@
+export type TContact = {
+  _id: string,
+  __v: number,
+  avatarUrl: string,
+  color: string,
+  country: string,
+  createdAt: string,
+  lastName: string,
+  name: string,
+  number: string,
+  owner: {
+    token: string,
+  },
+  updatedAt: string,
+}
+
+export type TContacts = TContact[] | []
+

+ 13 - 3
src/typescript/redux/controlApp/interfaces.ts

@@ -1,6 +1,16 @@
-export type TState = ('' | 'credentials' | 'search' | 'menu')
+import { TIsOpen } from './types'
+import { TContact } from '../contacts/types'
 
-export interface IPayload {
-  payload:TState
+export interface IControlAppState {
+  isOpen: TIsOpen,
+  companion: TContact
+}
+
+export interface IPayloadIsOpen {
+  payload:TIsOpen
+}
+
+export interface IPayloadCompanion {
+  payload:TContact
 }
 

+ 1 - 0
src/typescript/redux/controlApp/types.ts

@@ -0,0 +1 @@
+export type TIsOpen = ('' | 'credentials' | 'search' | 'menu')

+ 5 - 3
src/typescript/redux/interfaces.ts

@@ -1,8 +1,10 @@
-import {IAuthorizationState} from './authorization/interfaces'
-import { TState } from './controlApp/interfaces'
+import { IAuthorizationState } from './authorization/interfaces'
+import { IContactsState } from './contacts/interfaces'
+import { IControlAppState } from './controlApp/interfaces'
 
 export interface IState {
-  controlApp:TState,
+  contacts:IContactsState,
+  controlApp:IControlAppState,
   isLoading: boolean;
   authorization: IAuthorizationState
 }