From 9084e5f1859f1e818f45d2cab5b58f591db5aad0 Mon Sep 17 00:00:00 2001 From: medetkazizov Date: Mon, 24 Jun 2024 14:08:38 +0600 Subject: [PATCH 01/39] mark up organization page --- src/components/Organization.jsx | 494 ++++++++++++++++++++++++++++++++ src/components/User-info.jsx | 16 +- src/pages/profile-page.jsx | 8 + 3 files changed, 517 insertions(+), 1 deletion(-) create mode 100644 src/components/Organization.jsx diff --git a/src/components/Organization.jsx b/src/components/Organization.jsx new file mode 100644 index 00000000..b02d373a --- /dev/null +++ b/src/components/Organization.jsx @@ -0,0 +1,494 @@ +import React, { useMemo, useEffect } from 'react'; +import { + Avatar, + Box, + Button, + Typography, + Link, + useMediaQuery, + Tooltip, + ListItemText, + List, + ListItem, + Checkbox, + ListItemAvatar, +} from '@mui/material'; +import GitHubIcon from '@mui/icons-material/GitHub.js'; +import theme from '../styles/themes.js'; +import { useNavigate } from 'react-router-dom/dist'; +import { useDispatch, useSelector } from 'react-redux'; +import Loader from './Loader.jsx'; +import { AUDITOR, CUSTOMER } from '../redux/actions/types.js'; +import TagsList from './tagsList'; +import { ASSET_URL } from '../services/urls.js'; +import MobileTagsList from './MobileTagsList/index.jsx'; +import { addTestsLabel, capitalize } from '../lib/helper.js'; +import ShareProfileButton from './custom/ShareProfileButton.jsx'; +import IdentitySetting from './IdentitySetting/IdentitySetting.jsx'; +import LinkedinIcon from './icons/LinkedinIcon.jsx'; +import XTwitterLogo from './icons/XTwitter-logo.jsx'; +import { clearUserMessages } from '../redux/actions/userAction.js'; +import CustomSnackbar from './custom/CustomSnackbar.jsx'; +import Headings from '../router/Headings.jsx'; +import ListItemButton from '@mui/material/ListItemButton'; +import DeleteForeverRoundedIcon from '@mui/icons-material/DeleteForeverRounded'; + +const Organization = ({ role, linkId }) => { + const dispatch = useDispatch(); + const navigate = useNavigate(); + const matchXs = useMediaQuery(theme.breakpoints.down('xs')); + const matchXxs = useMediaQuery(theme.breakpoints.down(590)); + // + const { + customer, + error: customerError, + success: customerSuccess, + } = useSelector(s => s.customer); + const { + auditor, + error: auditorError, + success: auditorSuccess, + } = useSelector(s => s.auditor); + const { user, error } = useSelector(s => s.user); + + const handleEdit = () => { + navigate('/edit-profile'); + }; + + const data = useMemo(() => { + if (role === AUDITOR) { + return auditor; + } else { + return customer; + } + }, [role, customer, auditor]); + // + useEffect(() => { + if (linkId && data?.link_id && data?.link_id !== linkId) { + navigate(`/${role[0]}/${data.link_id}`); + } + }, [linkId, data]); + + const [checked, setChecked] = React.useState([1]); + + const handleToggle = value => () => { + const currentIndex = checked.indexOf(value); + const newChecked = [...checked]; + + if (currentIndex === -1) { + newChecked.push(value); + } else { + newChecked.splice(currentIndex, 1); + } + + setChecked(newChecked); + }; + + if (!data) { + return ; + } else { + return ( + + + + dispatch(clearUserMessages())} + /> + + + + + N + + + + + + Name + Org Name + + + Telegram + {data.contacts?.telegram} + + + E-mail + Org Email + + {role === AUDITOR && ( + + Price range: + {data?.price_range?.from && data?.price_range?.to && ( + + ${data?.price_range?.from} - {data?.price_range?.to} per + line + + )} + + )} + + + + + {['Mike', 'John', 'David', 'Viktor'].map(value => { + const labelId = `checkbox-list-secondary-label-${value}`; + return ( + + + + } + disablePadding + > + + + + + + + + ); + })} + + + + + + {/**/} + {/* About*/} + {/* */} + {/* {data.about}*/} + {/* */} + {/**/} + {!matchXs && } + {matchXs && } + + {user.linked_accounts?.map(account => { + if (account.name.toLowerCase() === 'linkedin') { + return ( + + + + + + + + ); + } else if (account.name.toLowerCase() === 'github') { + return ( + + + + + + + + ); + } else { + return ( + + + + + + + + ); + } + })} + + + {((role === AUDITOR && auditor.user_id) || + (role === CUSTOMER && customer.user_id)) && ( + + )} + + + + {/**/} + + + + ); + } +}; + +export default Organization; + +const aboutWrapper = theme => ({ + width: '100%', + '& span': { + marginRight: '50px', + maxWidth: '125px', + width: '100%', + }, + [theme.breakpoints.down('md')]: { + '& span': { + maxWidth: '90px', + marginRight: '20px', + }, + }, + [theme.breakpoints.down(450)]: { + '& span': { + maxWidth: '70px', + }, + }, +}); + +const wrapper = theme => ({ + width: '100%', + minHeight: '520px', + display: 'flex', + flexDirection: 'column', + padding: '60px 40px 40px', + gap: '30px', + justifyContent: 'space-between', + [theme.breakpoints.down('sm')]: { + gap: '20px', + padding: '20px', + }, + [theme.breakpoints.down('xs')]: { + width: '100%', + alignItems: 'center', + gap: '25px', + '& .mobile-tag-wrapper': { + maxWidth: '380px', + }, + }, +}); + +const infoInnerStyle = theme => ({ + display: 'flex', + flexDirection: 'column', + gap: '16px', + paddingTop: '10px', +}); + +const userListSx = theme => ({ + width: '100%', +}); + +const infoStyle = theme => ({ + display: 'flex', + margin: '0 0 50px', + flexDirection: 'row', + // flexWrap: 'wrap', + width: '100%', + gap: '40px', + rowGap: '15px', + '& .tagsWrapper': { + width: '100%', + }, + [theme.breakpoints.down('md')]: { + gap: '10px', + }, + [theme.breakpoints.down('sm')]: { + flexDirection: 'column', + gap: '16px', + margin: 0, + '& .tagsWrapper': { + width: '520px', + }, + }, +}); + +const avatarStyle = theme => ({ + width: '205px', + height: '205px', + [theme.breakpoints.down('xs')]: { + width: '150px', + height: '150px', + }, +}); + +const contentWrapper = theme => ({ + display: 'flex', + gap: '70px', + // justifyContent: 'space-between', + [theme.breakpoints.down('md')]: { + gap: '50px', + }, + [theme.breakpoints.down('sm')]: { + flexDirection: 'column', + alignItems: 'center', + gap: '40px', + }, +}); + +const buttonSx = theme => ({ + margin: '0 auto', + display: 'block', + color: theme.palette.background.default, + textTransform: 'capitalize', + fontWeight: 600, + fontSize: '18px', + // padding: '9px 50px', + width: '234px', + borderRadius: '10px', + [theme.breakpoints.down('xs')]: { + padding: '9px 10px', + }, +}); + +const submitAuditor = theme => ({ + backgroundColor: theme.palette.secondary.main, + '&:hover': { + backgroundColor: '#450e5d', + }, +}); + +const infoWrapper = theme => ({ + display: 'flex', + alignItems: 'center', + fontWeight: 500, + color: '#434242', + '& p': { + fontSize: 'inherit', + maxWidth: '250px', + }, + '& span': { + width: '125px', + marginRight: '50px', + color: '#B2B3B3', + }, + fontSize: '15px', + [theme.breakpoints.down('md')]: { + '& span': { + width: '90px', + marginRight: '20px', + }, + '& p': { + maxWidth: '190px', + }, + }, + [theme.breakpoints.down('sm')]: { + '& p': { + maxWidth: '240px', + }, + }, + [theme.breakpoints.down('xs')]: { + fontSize: '12px', + }, + [theme.breakpoints.down(450)]: { + '& span': { + width: '70px', + marginRight: '20px', + }, + '& p': { + maxWidth: '180px', + }, + }, +}); + +const accountLink = { + height: '30px', + display: 'flex', + alignItems: 'center', + color: 'black', + textDecoration: 'none', +}; diff --git a/src/components/User-info.jsx b/src/components/User-info.jsx index 71b3d62e..68d6d480 100644 --- a/src/components/User-info.jsx +++ b/src/components/User-info.jsx @@ -109,6 +109,17 @@ const UserInfo = ({ role, linkId }) => { sx={avatarStyle} alt="User photo" /> + + + N + + + S + + + N + + @@ -253,6 +264,9 @@ const UserInfo = ({ role, linkId }) => { Edit + @@ -364,7 +378,7 @@ const buttonSx = theme => ({ fontWeight: 600, fontSize: '18px', // padding: '9px 50px', - width: '214px', + width: '234px', borderRadius: '10px', [theme.breakpoints.down('xs')]: { padding: '9px 10px', diff --git a/src/pages/profile-page.jsx b/src/pages/profile-page.jsx index dfc88539..5d3e4e53 100644 --- a/src/pages/profile-page.jsx +++ b/src/pages/profile-page.jsx @@ -15,6 +15,7 @@ import CustomSnackbar from '../components/custom/CustomSnackbar.jsx'; import { isAuth } from '../lib/helper.js'; import PublicProfile from './Public-profile.jsx'; import NotFound from './Not-Found.jsx'; +import Organization from '../components/Organization.jsx'; const ProfilePage = () => { const dispatch = useDispatch(); @@ -91,6 +92,9 @@ const ProfilePage = () => { {chooseTab === 'user-info' && ( )} + {chooseTab === 'organizations' && ( + + )} @@ -127,6 +131,10 @@ const customerTabs = [ value: 'user-info', label: 'User info', }, + { + value: 'organizations', + label: 'My organizations', + }, ]; const wrapper = theme => ({ From 8a64a29f5e9dedb43426ce97ea73374fa3422344 Mon Sep 17 00:00:00 2001 From: medetkazizov Date: Tue, 25 Jun 2024 17:02:28 +0600 Subject: [PATCH 02/39] mark up of choose type of chat cart --- src/components/AuditorModal.jsx | 53 ++++++++++++++++++-------------- src/components/Chat/TypeChat.jsx | 34 ++++++++++++++++++++ src/components/Organization.jsx | 15 +++------ 3 files changed, 68 insertions(+), 34 deletions(-) create mode 100644 src/components/Chat/TypeChat.jsx diff --git a/src/components/AuditorModal.jsx b/src/components/AuditorModal.jsx index 1e7303a7..fe0b1373 100644 --- a/src/components/AuditorModal.jsx +++ b/src/components/AuditorModal.jsx @@ -30,6 +30,7 @@ import ShareProfileButton from './custom/ShareProfileButton.jsx'; import PriceCalculation from './PriceCalculation.jsx'; import { setCurrentChat } from '../redux/actions/chatActions.js'; import ChatIcon from './icons/ChatIcon.jsx'; +import TypeChat from './Chat/TypeChat.jsx'; export default function AuditorModal({ open, @@ -49,6 +50,7 @@ export default function AuditorModal({ const { user } = useSelector(s => s.user); const { chatList } = useSelector(s => s.chat); const myProjects = useSelector(state => state.project.myProjects); + const [isOpenType, setIsOpenType] = useState(false); const [mode, setMode] = useState('info'); const [message, setMessage] = useState(''); @@ -87,29 +89,33 @@ export default function AuditorModal({ }; const handleSendMessage = () => { - window.scrollTo(0, 0); - - const existingChat = chatList.find(chat => - chat.members?.find( - member => - member.id === auditor?.user_id && - member.role?.toLowerCase() === AUDITOR, - ), - ); - const chatId = existingChat ? existingChat.id : auditor?.user_id; - const members = [auditor?.user_id, user.id]; - - dispatch( - setCurrentChat(chatId, { - name: auditor.first_name, - avatar: auditor.avatar, - role: AUDITOR, - isNew: !existingChat, - members, - }), - ); - localStorage.setItem('path', window.location.pathname); - navigate(`/chat/${existingChat ? existingChat.id : auditor?.user_id}`); + if (true) { + setIsOpenType(true); + } else { + window.scrollTo(0, 0); + + const existingChat = chatList.find(chat => + chat.members?.find( + member => + member.id === auditor?.user_id && + member.role?.toLowerCase() === AUDITOR, + ), + ); + const chatId = existingChat ? existingChat.id : auditor?.user_id; + const members = [auditor?.user_id, user.id]; + + dispatch( + setCurrentChat(chatId, { + name: auditor.first_name, + avatar: auditor.avatar, + role: AUDITOR, + isNew: !existingChat, + members, + }), + ); + localStorage.setItem('path', window.location.pathname); + navigate(`/chat/${existingChat ? existingChat.id : auditor?.user_id}`); + } }; useEffect(() => { @@ -259,6 +265,7 @@ export default function AuditorModal({ {...addTestsLabel('message-button')} > + {isOpenType && } )} diff --git a/src/components/Chat/TypeChat.jsx b/src/components/Chat/TypeChat.jsx new file mode 100644 index 00000000..a9eff25b --- /dev/null +++ b/src/components/Chat/TypeChat.jsx @@ -0,0 +1,34 @@ +import React from 'react'; +import { Box, Button, Typography } from '@mui/material'; + +const TypeChat = () => { + return ( + + + + + ); +}; + +export default TypeChat; diff --git a/src/components/Organization.jsx b/src/components/Organization.jsx index b02d373a..20257575 100644 --- a/src/components/Organization.jsx +++ b/src/components/Organization.jsx @@ -151,17 +151,10 @@ const Organization = ({ role, linkId }) => { E-mail Org Email - {role === AUDITOR && ( - - Price range: - {data?.price_range?.from && data?.price_range?.to && ( - - ${data?.price_range?.from} - {data?.price_range?.to} per - line - - )} - - )} + + Type + Customer organization + From 4f67cf37e19cb712e32dee841fc65c8a6de4d604 Mon Sep 17 00:00:00 2001 From: medetkazizov Date: Wed, 3 Jul 2024 15:35:50 +0600 Subject: [PATCH 03/39] chat for organization --- src/components/AuditorModal.jsx | 1 + src/components/Chat/TypeChat.jsx | 28 +++++++++++++- src/pages/ChatPage.jsx | 63 +++++++++++++++++++++++++++++++- 3 files changed, 89 insertions(+), 3 deletions(-) diff --git a/src/components/AuditorModal.jsx b/src/components/AuditorModal.jsx index fe0b1373..440d1d86 100644 --- a/src/components/AuditorModal.jsx +++ b/src/components/AuditorModal.jsx @@ -89,6 +89,7 @@ export default function AuditorModal({ }; const handleSendMessage = () => { + // TODO add check for pm or org chat if (true) { setIsOpenType(true); } else { diff --git a/src/components/Chat/TypeChat.jsx b/src/components/Chat/TypeChat.jsx index a9eff25b..b294c374 100644 --- a/src/components/Chat/TypeChat.jsx +++ b/src/components/Chat/TypeChat.jsx @@ -1,7 +1,9 @@ -import React from 'react'; +import React, { useState } from 'react'; import { Box, Button, Typography } from '@mui/material'; const TypeChat = () => { + const [isOpen, setIsOpen] = useState(false); + return ( { bgcolor: 'background.paper', boxShadow: 24, borderRadius: '10px', + overflow: 'visible', p: 4, }} > @@ -23,9 +26,30 @@ const TypeChat = () => { + + + + )} ); diff --git a/src/pages/ChatPage.jsx b/src/pages/ChatPage.jsx index df8fc769..00e05fb0 100644 --- a/src/pages/ChatPage.jsx +++ b/src/pages/ChatPage.jsx @@ -1,7 +1,7 @@ import React, { useState, useEffect } from 'react'; import { useDispatch, useSelector } from 'react-redux'; import { useParams } from 'react-router-dom'; -import { Box, Button, IconButton, useMediaQuery } from '@mui/material'; +import { Avatar, Box, Button, IconButton, useMediaQuery } from '@mui/material'; import Layout from '../styles/Layout.jsx'; import { CustomCard } from '../components/custom/Card'; import ChatList from '../components/Chat/ChatList.jsx'; @@ -95,6 +95,66 @@ const ChatPage = () => { + + + + PM + + + + + Org1 + + + + + Org1 + + + + + Org1 + + + ({ padding: '20px 40px 100px', position: 'relative', display: 'flex', + maxWidth: 'unset', flexDirection: 'column', alignItems: 'flex-start', gap: '15px', From 4f28a1b146e0fc21218cdf60a5843bce8c9f661a Mon Sep 17 00:00:00 2001 From: medetkazizov Date: Tue, 23 Jul 2024 17:38:47 +0600 Subject: [PATCH 04/39] rework of user page with organization added page for organizations, and connected backend to get edit and create organization --- .../IdentitySetting/IdentitySetting.jsx | 2 +- src/components/Organization.jsx | 513 ++++++++++-------- src/components/User-info.jsx | 78 ++- src/components/custom/info-card.jsx | 7 +- .../CreateEditOrganizationForm.jsx | 381 +++++++++++++ src/components/header/UserMenu.jsx | 16 + src/pages/CreateEditOrganization.jsx | 52 ++ src/pages/MyOrganizations.jsx | 231 ++++++++ src/pages/profile-page.jsx | 7 - src/redux/actions/organizationAction.js | 94 ++++ src/redux/actions/types.js | 5 + src/redux/reducers/organizationReducer.js | 52 ++ src/redux/store.js | 2 + src/routes/AppRoutes.jsx | 46 ++ 14 files changed, 1230 insertions(+), 256 deletions(-) create mode 100644 src/components/forms/create-edit-organization-form/CreateEditOrganizationForm.jsx create mode 100644 src/pages/CreateEditOrganization.jsx create mode 100644 src/pages/MyOrganizations.jsx create mode 100644 src/redux/actions/organizationAction.js create mode 100644 src/redux/reducers/organizationReducer.js diff --git a/src/components/IdentitySetting/IdentitySetting.jsx b/src/components/IdentitySetting/IdentitySetting.jsx index af08fc8e..53206e93 100644 --- a/src/components/IdentitySetting/IdentitySetting.jsx +++ b/src/components/IdentitySetting/IdentitySetting.jsx @@ -342,7 +342,7 @@ const buttonSx = theme => ({ fontWeight: 600, fontSize: '18px', // padding: '9px 50px', - width: '214px', + width: '234px', borderRadius: '10px', [theme.breakpoints.down('xs')]: { padding: '9px 10px', diff --git a/src/components/Organization.jsx b/src/components/Organization.jsx index 20257575..812f10b1 100644 --- a/src/components/Organization.jsx +++ b/src/components/Organization.jsx @@ -14,8 +14,9 @@ import { ListItemAvatar, } from '@mui/material'; import GitHubIcon from '@mui/icons-material/GitHub.js'; +import ArrowBackIcon from '@mui/icons-material/ArrowBack.js'; import theme from '../styles/themes.js'; -import { useNavigate } from 'react-router-dom/dist'; +import { useNavigate, useParams } from 'react-router-dom/dist'; import { useDispatch, useSelector } from 'react-redux'; import Loader from './Loader.jsx'; import { AUDITOR, CUSTOMER } from '../redux/actions/types.js'; @@ -32,13 +33,23 @@ import CustomSnackbar from './custom/CustomSnackbar.jsx'; import Headings from '../router/Headings.jsx'; import ListItemButton from '@mui/material/ListItemButton'; import DeleteForeverRoundedIcon from '@mui/icons-material/DeleteForeverRounded'; +import Layout from '../styles/Layout.jsx'; +import { CustomCard } from './custom/Card.jsx'; +import { + clearOrganization, + getOrganizationById, +} from '../redux/actions/organizationAction.js'; +import InfoCard from './custom/info-card.jsx'; -const Organization = ({ role, linkId }) => { +const Organization = () => { + const { id: linkId } = useParams(); + const role = useSelector(s => s.user.user.current_role); const dispatch = useDispatch(); const navigate = useNavigate(); const matchXs = useMediaQuery(theme.breakpoints.down('xs')); const matchXxs = useMediaQuery(theme.breakpoints.down(590)); - // + const organization = useSelector(s => s.organization.organization); + const { customer, error: customerError, @@ -51,8 +62,15 @@ const Organization = ({ role, linkId }) => { } = useSelector(s => s.auditor); const { user, error } = useSelector(s => s.user); + useEffect(() => { + dispatch(getOrganizationById(linkId)); + return () => { + dispatch(clearOrganization()); + }; + }, [linkId]); + const handleEdit = () => { - navigate('/edit-profile'); + navigate(`/edit-organization/${linkId}`); }; const data = useMemo(() => { @@ -69,250 +87,285 @@ const Organization = ({ role, linkId }) => { } }, [linkId, data]); - const [checked, setChecked] = React.useState([1]); - - const handleToggle = value => () => { - const currentIndex = checked.indexOf(value); - const newChecked = [...checked]; - - if (currentIndex === -1) { - newChecked.push(value); - } else { - newChecked.splice(currentIndex, 1); - } - - setChecked(newChecked); - }; - - if (!data) { - return ; + if (!organization.id) { + return ( + + + + + + + ); } else { return ( - - + + + + + + {/**/} - dispatch(clearUserMessages())} - /> + dispatch(clearUserMessages())} + /> - - - - N - - - - - - Name - Org Name - - - Telegram - {data.contacts?.telegram} - - - E-mail - Org Email - - - Type - Customer organization - - - - - + - {['Mike', 'John', 'David', 'Viktor'].map(value => { - const labelId = `checkbox-list-secondary-label-${value}`; + + N + + + + + + Name + {organization.name} + + + Telegram + + {organization.contacts?.telegram} + + + + E-mail + + {organization.contacts?.email} + + + + Type + + {organization.organization_type} + + + + + + + {organization.members.map(value => { + const labelId = `checkbox-list-secondary-label-${value}`; + return ( + + + + } + disablePadding + > + + + + + + + + ); + })} + + + + + + + {!matchXs && ( + + )} + {matchXs && } + + {user.linked_accounts?.map(account => { + if (account.name.toLowerCase() === 'linkedin') { return ( - - - - } - disablePadding + - - - + + + + + + ); + } else if (account.name.toLowerCase() === 'github') { + return ( + + + + - - - - + + + + ); + } else { + return ( + + + + + + + ); - })} - + } + })} - - - - {/**/} - {/* About*/} - {/* */} - {/* {data.about}*/} - {/* */} - {/**/} - {!matchXs && } - {matchXs && } - - {user.linked_accounts?.map(account => { - if (account.name.toLowerCase() === 'linkedin') { - return ( - + - - {/**/} - - - + Edit + + {/**/} + + + + + ); } }; export default Organization; +const wrapper = theme => ({ + display: 'flex', + flexDirection: 'column', + maxWidth: '1300px', + width: '100%', + '& ul': { + fontSize: '16px', + marginBottom: '28px', + '& li': { + marginLeft: '15px', + marginTop: '7px', + }, + }, +}); + const aboutWrapper = theme => ({ width: '100%', '& span': { @@ -333,7 +386,7 @@ const aboutWrapper = theme => ({ }, }); -const wrapper = theme => ({ +const innerWrapper = theme => ({ width: '100%', minHeight: '520px', display: 'flex', diff --git a/src/components/User-info.jsx b/src/components/User-info.jsx index 68d6d480..5a24b05c 100644 --- a/src/components/User-info.jsx +++ b/src/components/User-info.jsx @@ -4,13 +4,12 @@ import { Box, Button, Typography, - Link, useMediaQuery, Tooltip, } from '@mui/material'; import GitHubIcon from '@mui/icons-material/GitHub.js'; import theme from '../styles/themes.js'; -import { useNavigate } from 'react-router-dom/dist'; +import { useNavigate, Link } from 'react-router-dom/dist'; import { useDispatch, useSelector } from 'react-redux'; import Loader from './Loader.jsx'; import { AUDITOR, CUSTOMER } from '../redux/actions/types.js'; @@ -30,7 +29,8 @@ const UserInfo = ({ role, linkId }) => { const dispatch = useDispatch(); const navigate = useNavigate(); const matchXs = useMediaQuery(theme.breakpoints.down('xs')); - const matchXxs = useMediaQuery(theme.breakpoints.down(590)); + const matchXxs = useMediaQuery(theme.breakpoints.down(850)); + const organizations = useSelector(s => s.organization.organizations); const { customer, @@ -109,17 +109,61 @@ const UserInfo = ({ role, linkId }) => { sx={avatarStyle} alt="User photo" /> - - - N - - - S - - - N - - + {!!organizations.length && ( + + + Organization + + + {organizations?.map(org => { + if (org.avatar) { + return ( + + + + + + ); + } else { + return ( + + + + {org.name} + + + + ); + } + })} + + + )} @@ -264,7 +308,11 @@ const UserInfo = ({ role, linkId }) => { Edit - diff --git a/src/components/custom/info-card.jsx b/src/components/custom/info-card.jsx index e5c31bb0..9d2e1b1c 100644 --- a/src/components/custom/info-card.jsx +++ b/src/components/custom/info-card.jsx @@ -1,10 +1,11 @@ import React from 'react'; import { Box } from '@mui/material'; import theme from '../../styles/themes.js'; - -const InfoCard = ({ children, role = 'customer' }) => { +const InfoCard = ({ children, role = 'customer', sx }) => { return ( - + {children} ); diff --git a/src/components/forms/create-edit-organization-form/CreateEditOrganizationForm.jsx b/src/components/forms/create-edit-organization-form/CreateEditOrganizationForm.jsx new file mode 100644 index 00000000..9df1b41a --- /dev/null +++ b/src/components/forms/create-edit-organization-form/CreateEditOrganizationForm.jsx @@ -0,0 +1,381 @@ +import React, { useEffect, useMemo, useState } from 'react'; +import { useDispatch, useSelector } from 'react-redux'; +import { Field, Form, Formik } from 'formik'; +import * as Yup from 'yup'; +import { + Box, + Button, + Checkbox, + Typography, + useMediaQuery, +} from '@mui/material'; +import SimpleField from '../fields/simple-field.jsx'; +import theme from '../../../styles/themes.js'; +import Loader from '../../Loader.jsx'; +import { AUDITOR, CUSTOMER } from '../../../redux/actions/types.js'; +import AvatarForm from '../Avatar-form/index.jsx'; +import { SliderRange } from '../salary-slider/slider-range.jsx'; +import { addTestsLabel } from '../../../lib/helper.js'; +import { useNavigate } from 'react-router-dom/dist'; +import ArrowBackIcon from '@mui/icons-material/ArrowBack'; +import { history } from '../../../services/history.js'; +import { + createOrganization, + updateOrganization, +} from '../../../redux/actions/organizationAction.js'; + +const GoBack = ({ role, newLinkId }) => { + const navigate = useNavigate(); + const handleGoBack = () => { + navigate(-1); + }; + return ( + + ); +}; + +const CreateEditOrganizationForm = ({ role, organization }) => { + const matchSm = useMediaQuery(theme.breakpoints.down('sm')); + const matchXs = useMediaQuery(theme.breakpoints.down('xs')); + const dispatch = useDispatch(); + const { user } = useSelector(s => s.user); + const { customer } = useSelector(s => s.customer); + const { auditor } = useSelector(s => s.auditor); + const navigate = useNavigate(); + const [isDirty, setIsDirty] = useState(false); + + if (!organization.id) { + return ; + } else { + return ( + { + setIsDirty(false); + if (values.id) { + dispatch(updateOrganization(values, navigateTo)); + } else { + dispatch(createOrganization(values, navigateTo)); + } + }} + > + {({ handleSubmit, values, setFieldValue, dirty }) => { + useEffect(() => { + setIsDirty(dirty); + }, [dirty]); + useEffect(() => { + const unblock = history.block(({ location }) => { + if (!isDirty) { + unblock(); + return navigate(location); + } + + const confirmed = window.confirm( + 'Do you want to save changes before leaving the page?', + ); + + if (confirmed) { + handleSubmit(values); + unblock(); + return navigate(location); + } else { + unblock(); + return navigate(location); + } + }); + + if (!isDirty) { + unblock(); + } + + return () => { + unblock(); + }; + }, [history, isDirty]); + return ( +
+ + + + + + + {matchSm && ( + + + + )} + + + + {!matchSm && ( + <> + + {/**/} + + )} + + + + + { + setFieldValue( + 'contacts.public_contacts', + e.target.checked, + ); + }} + inputProps={{ + ...addTestsLabel('make-contacts-visible'), + }} + /> + + + + + + + {role !== CUSTOMER && ( + + + Price per line of code + + { + const value = Array.isArray(newValue) + ? newValue + : [newValue, newValue]; + setFieldValue('price_range.from', value[0]); + setFieldValue('price_range.to', value[1]); + }} + /> + + )} + + + + +
+ ); + }} +
+ ); + } +}; + +export default CreateEditOrganizationForm; + +const EditOrganizationSchema = Yup.object().shape({ + name: Yup.string(), + contacts: Yup.object().shape({ + email: Yup.string().email('Invalid email').required('required'), + telegram: Yup.string(), + }), +}); + +const backBtnSx = theme => ({ + position: 'absolute', + left: '-70px', + top: '-30px', + [theme.breakpoints.down('sm')]: { + left: '-50px', + }, + [theme.breakpoints.down('xs')]: { + left: '-40px', + }, +}); + +const wrapper = theme => ({ + display: 'flex', + position: 'relative', + gap: '52px', + [theme.breakpoints.down('sm')]: { + flexDirection: 'column', + gap: '12px', + }, +}); + +const rateLabel = theme => ({ + fontSize: '15px', + color: '#B2B3B3', + fontWeight: 500, +}); + +const fieldsWrapper = theme => ({ + display: 'flex', + gap: '52px', + width: '100%', + '& p': { + color: '#B2B3B3', + }, + [theme.breakpoints.down('sm')]: { + flexDirection: 'column', + gap: '14px', + }, +}); + +const fieldWrapper = theme => ({ + width: '100%', + gap: '18px', + display: 'flex', + flexDirection: 'column', + '& .tags-array-wrapper': { + margin: 'auto 0 0 0', + }, + '& .password-wrapper': { + gap: '12px', + }, + '& .field-wrapper': { + gap: '12px', + }, + [theme.breakpoints.down('sm')]: { + gap: '14px', + '& .field-wrapper': { + gap: '8px', + }, + '& .password-wrapper': { + gap: '8px', + }, + }, + [theme.breakpoints.down('xs')]: { + '& p': { + fontSize: '12px', + }, + '& input': { + fontSize: '12px', + }, + }, +}); + +const buttonSx = { + display: 'block', + margin: '70px auto 0', + textTransform: 'unset', + padding: '13px 0', + fontWeight: 600, + fontSize: '14px', + lineHeight: 1.2, + width: '200px', + borderRadius: '10px', +}; + +const avatarWrapper = theme => ({ + '& button': { + textTransform: 'unset', + '& svg': { marginRight: '5px' }, + }, + '& .MuiAvatar-root': { + width: '205px', + height: '205px', + }, + [theme.breakpoints.down('md')]: { + '& .MuiAvatar-root': { + width: '158px', + height: '158px', + }, + '& button': { fontSize: '12px' }, + }, + [theme.breakpoints.down('sm')]: { + '& .MuiAvatar-root': { + width: '128px', + height: '128px', + }, + display: 'flex', + gap: '22px', + '& p': { color: '#B2B3B3' }, + }, + [theme.breakpoints.down('xs')]: { + '& .MuiAvatar-root': { + width: '80px', + height: '80px', + }, + '& button': { + paddingX: '4px', + fontSize: '10px', + '& svg': { marginRight: 0 }, + }, + }, +}); diff --git a/src/components/header/UserMenu.jsx b/src/components/header/UserMenu.jsx index 94e9bd45..ee58d475 100644 --- a/src/components/header/UserMenu.jsx +++ b/src/components/header/UserMenu.jsx @@ -26,6 +26,7 @@ export const UserMenu = ({ open, handleClose, anchor, userAvatar, pages }) => { const { customer } = useSelector(s => s.customer); const navigate = useNavigate(); const matchSm = useMediaQuery(theme.breakpoints.down(1080)); + const organizations = useSelector(s => s.organization.organizations); const currentRole = useSelector(s => s.user.user.current_role); const { differentRoleUnreadMessages } = useSelector(s => s.chat); @@ -49,6 +50,10 @@ export const UserMenu = ({ open, handleClose, anchor, userAvatar, pages }) => { navigate(`/${rolePrefix}/${link_id}`); }; + const handleMyOrganizationClick = () => { + navigate(`/my-organizations`); + }; + return ( { My Account + {!!organizations.length && ( + + + + )} {matchSm && pages?.map(el => el.menuOptions diff --git a/src/pages/CreateEditOrganization.jsx b/src/pages/CreateEditOrganization.jsx new file mode 100644 index 00000000..ea267b38 --- /dev/null +++ b/src/pages/CreateEditOrganization.jsx @@ -0,0 +1,52 @@ +import React, { useEffect, useState } from 'react'; +import { useDispatch, useSelector } from 'react-redux'; +import Layout from '../styles/Layout.jsx'; +import { CustomCard } from '../components/custom/Card.jsx'; +import Headings from '../router/Headings.jsx'; +import CreateEditOrganizationForm from '../components/forms/create-edit-organization-form/CreateEditOrganizationForm.jsx'; +import { useParams } from 'react-router-dom/dist'; +import { + clearOrganization, + getOrganizationById, +} from '../redux/actions/organizationAction.js'; + +const CreateEditOrganization = () => { + const { id } = useParams(); + const dispatch = useDispatch(); + const role = useSelector(s => s.user.user.current_role); + const organization = useSelector(s => s.organization.organization); + + useEffect(() => { + if (id) { + dispatch(getOrganizationById(id)); + } + return () => { + dispatch(clearOrganization()); + }; + }, [id]); + + return ( + + + + + + + + ); +}; + +export default CreateEditOrganization; + +const editWrapper = theme => ({ + padding: '41px 68px 70px', + [theme.breakpoints.down('sm')]: { + padding: '41px 48px 50px', + }, + [theme.breakpoints.down('xs')]: { + padding: '31px 28px 40px', + }, +}); diff --git a/src/pages/MyOrganizations.jsx b/src/pages/MyOrganizations.jsx new file mode 100644 index 00000000..19b1967f --- /dev/null +++ b/src/pages/MyOrganizations.jsx @@ -0,0 +1,231 @@ +import React, { useMemo, useEffect } from 'react'; +import { + Avatar, + Box, + Button, + Typography, + useMediaQuery, + Tooltip, +} from '@mui/material'; +import ArrowBackIcon from '@mui/icons-material/ArrowBack.js'; +import theme from '../styles/themes.js'; +import { useNavigate, useParams, Link } from 'react-router-dom/dist'; +import { useDispatch, useSelector } from 'react-redux'; +import { AUDITOR, CUSTOMER } from '../redux/actions/types.js'; +import { ASSET_URL } from '../services/urls.js'; +import Headings from '../router/Headings.jsx'; +import Layout from '../styles/Layout.jsx'; +import Loader from '../components/Loader.jsx'; +import { CustomCard } from '../components/custom/Card.jsx'; +import InfoCard from '../components/custom/info-card.jsx'; + +const MyOrganization = () => { + const role = useSelector(s => s.user.user.current_role); + const dispatch = useDispatch(); + const navigate = useNavigate(); + const matchXs = useMediaQuery(theme.breakpoints.down('xs')); + const matchXxs = useMediaQuery(theme.breakpoints.down(590)); + const own = useSelector(s => s.organization.own); + const organizations = useSelector(s => s.organization.includeMe); + + const { + customer, + error: customerError, + success: customerSuccess, + } = useSelector(s => s.customer); + const { + auditor, + error: auditorError, + success: auditorSuccess, + } = useSelector(s => s.auditor); + const { user, error } = useSelector(s => s.user); + + if (!organizations.length && !own.length) { + return ( + + + + + + + ); + } else { + return ( + + + + + + + {!!own.length && ( + + My organizations + + {own?.map(org => { + return ( + + + + + + {org.name} + + + + + ); + })} + + + )} + {!!own.length && ( + + Organizations + + {organizations?.map(org => { + return ( + + + + + + {org.name} + + + + + ); + })} + + + )} + + + + + + ); + } +}; + +export default MyOrganization; + +const wrapper = theme => ({ + display: 'flex', + flexDirection: 'column', + maxWidth: '1300px', + width: '100%', + '& ul': { + fontSize: '16px', + marginBottom: '28px', + '& li': { + marginLeft: '15px', + marginTop: '7px', + }, + }, +}); + +const innerWrapper = theme => ({ + width: '100%', + minHeight: '520px', + display: 'flex', + flexDirection: 'column', + padding: '60px 40px 40px', + gap: '30px', + justifyContent: 'space-between', + [theme.breakpoints.down('sm')]: { + gap: '20px', + padding: '20px', + paddingTop: '55px', + + '& h4': { + fontSize: '25px', + }, + }, + [theme.breakpoints.down('xs')]: { + width: '100%', + gap: '25px', + '& h4': { + fontSize: '20px', + }, + '& .mobile-tag-wrapper': { + maxWidth: '380px', + }, + }, +}); + +const avatarSx = theme => ({ + height: '100px', + width: '100px', + padding: '5px', + border: `1px solid ${theme.palette.primary.main}`, + [theme.breakpoints.down('sm')]: { + width: '80px', + height: '80px', + }, + [theme.breakpoints.down('xs')]: { + width: '50px', + height: '50px', + }, +}); + +const organizationSx = theme => ({ + display: 'flex', + flexDirection: 'column', + alignItems: 'center', + [theme.breakpoints.down('sm')]: { + '& p': { + fontSize: '14px', + }, + }, +}); + +const contentWrapper = theme => ({ + display: 'flex', + gap: '30px', + flexDirection: 'column', + alignItems: 'center', + '& a': { + textDecoration: 'none', + color: 'black', + }, +}); diff --git a/src/pages/profile-page.jsx b/src/pages/profile-page.jsx index 5d3e4e53..239d3d01 100644 --- a/src/pages/profile-page.jsx +++ b/src/pages/profile-page.jsx @@ -92,9 +92,6 @@ const ProfilePage = () => { {chooseTab === 'user-info' && ( )} - {chooseTab === 'organizations' && ( - - )} @@ -131,10 +128,6 @@ const customerTabs = [ value: 'user-info', label: 'User info', }, - { - value: 'organizations', - label: 'My organizations', - }, ]; const wrapper = theme => ({ diff --git a/src/redux/actions/organizationAction.js b/src/redux/actions/organizationAction.js new file mode 100644 index 00000000..557b95b0 --- /dev/null +++ b/src/redux/actions/organizationAction.js @@ -0,0 +1,94 @@ +import Cookies from 'js-cookie'; +import axios from 'axios'; +import { + CLEAR_ORGANIZATION, + CREATE_ORGANIZATION, + GET_MY_ORGANIZATION, + GET_ORGANIZATION_BY_ID, + UPDATE_ORGANIZATION, +} from './types.js'; +import { history } from '../../services/history.js'; + +const API_URL = import.meta.env.VITE_API_BASE_URL; + +export const createOrganization = (value, navigateTo) => { + return dispatch => { + const token = Cookies.get('token'); + axios + .post(`${API_URL}/organization`, value, { + headers: { + Authorization: `Bearer ${token}`, + }, + }) + .then(({ data }) => { + dispatch({ type: CREATE_ORGANIZATION, payload: data }); + if (navigateTo) { + history.push({ pathname: navigateTo }, { some: true }); + } + }) + .catch(({ response }) => { + console.log(response, 'res'); + }); + }; +}; + +export const updateOrganization = (value, navigateTo) => { + return dispatch => { + const token = Cookies.get('token'); + axios + .patch(`${API_URL}/organization/${value.id}`, value, { + headers: { + Authorization: `Bearer ${token}`, + }, + }) + .then(({ data }) => { + dispatch({ type: UPDATE_ORGANIZATION, payload: data }); + if (navigateTo) { + history.push({ pathname: navigateTo }, { some: true }); + } + }) + .catch(({ response }) => { + console.log(response, 'res'); + }); + }; +}; + +export const clearOrganization = () => { + return dispatch => { + dispatch({ type: CLEAR_ORGANIZATION }); + }; +}; + +export const getMyOrganizations = () => { + return dispatch => { + const token = Cookies.get('token'); + axios + .get(`${API_URL}/my_organizations`, { + headers: { + Authorization: `Bearer ${token}`, + }, + }) + .then(({ data }) => { + dispatch({ type: GET_MY_ORGANIZATION, payload: data }); + }) + .catch(({ response }) => { + console.log(response, 'res'); + }); + }; +}; + +export const getOrganizationById = id => { + return dispatch => { + const token = Cookies.get('token'); + axios + .get(`${API_URL}/organization/${id}`, { + headers: { + Authorization: `Bearer ${token}`, + }, + }) + .then(({ data }) => { + console.log(data); + dispatch({ type: GET_ORGANIZATION_BY_ID, payload: data }); + }); + }; +}; diff --git a/src/redux/actions/types.js b/src/redux/actions/types.js index 028151b4..0815737d 100644 --- a/src/redux/actions/types.js +++ b/src/redux/actions/types.js @@ -145,6 +145,11 @@ export const EDIT_AUDIT_REQUEST_CUSTOMER = 'EDIT_AUDIT_REQUEST_CUSTOMER'; export const NEED_TO_AUTH_GITHUB = 'NEED_TO_AUTH_GITHUB'; export const SWITCH_REPO = 'SWITCH_REPO'; export const NOT_FOUND_REPOS = 'NOT_FOUND_REPOS'; +export const CREATE_ORGANIZATION = 'CREATE_ORGANIZATION'; +export const UPDATE_ORGANIZATION = 'UPDATE_ORGANIZATION'; +export const CLEAR_ORGANIZATION = 'CLEAR_ORGANIZATION'; +export const GET_ORGANIZATION_BY_ID = 'GET_ORGANIZATION_BY_ID'; +export const GET_MY_ORGANIZATION = 'GET_MY_ORGANIZATION'; export const CLEAR_NOT_FOUND_ERROR = 'CLEAR_NOT_FOUND_ERROR'; export const GET_MY_PRIVATE_GITHUB_ORGANIZATION = 'GET_MY_PRIVATE_GITHUB_ORGANIZATION'; diff --git a/src/redux/reducers/organizationReducer.js b/src/redux/reducers/organizationReducer.js new file mode 100644 index 00000000..150d5b51 --- /dev/null +++ b/src/redux/reducers/organizationReducer.js @@ -0,0 +1,52 @@ +import { + CREATE_ORGANIZATION, + GET_MY_ORGANIZATION, + GET_ORGANIZATION_BY_ID, + UPDATE_ORGANIZATION, + CLEAR_ORGANIZATION, +} from '../actions/types.js'; + +const initialState = { + organizations: [], + own: [], + includeMe: [], + organization: {}, +}; + +export const organizationReducer = (state = initialState, action) => { + switch (action.type) { + case GET_MY_ORGANIZATION: + return { + ...state, + organizations: [...action.payload.owner, ...action.payload.member], + own: action.payload.owner, + includeMe: action.payload.member, + }; + case CREATE_ORGANIZATION: + return { + ...state, + organization: [...state.organization, action.payload], + own: [...state.own, action.payload], + }; + case UPDATE_ORGANIZATION: + return { + ...state, + organization: action.payload, + own: state.own.map(el => + el.id === action.payload.id ? action.payload : el, + ), + }; + case GET_ORGANIZATION_BY_ID: + return { + ...state, + organization: action.payload, + }; + case CLEAR_ORGANIZATION: + return { + ...state, + organization: {}, + }; + default: + return state; + } +}; diff --git a/src/redux/store.js b/src/redux/store.js index 2e09ea08..9d74c127 100644 --- a/src/redux/store.js +++ b/src/redux/store.js @@ -14,6 +14,7 @@ import { notFoundReducer } from './reducers/notFoundReducer.js'; import { chatReducer } from './reducers/chatReducer.js'; import { githubReducer } from './reducers/githubReducer.js'; import { filterConfig } from './reducers/configReducer.js'; +import { organizationReducer } from './reducers/organizationReducer.js'; export const store = createStore( combineReducers({ @@ -29,6 +30,7 @@ export const store = createStore( chat: chatReducer, github: githubReducer, filter: filterConfig, + organization: organizationReducer, }), composeWithDevTools(applyMiddleware(thunk, websocketMiddleware())), ); diff --git a/src/routes/AppRoutes.jsx b/src/routes/AppRoutes.jsx index 5ebf02e8..4ba1e26d 100644 --- a/src/routes/AppRoutes.jsx +++ b/src/routes/AppRoutes.jsx @@ -54,6 +54,10 @@ import Headings from '../router/Headings.jsx'; import { AUDITOR, CUSTOMER } from '../redux/actions/types.js'; import UserProjects from '../pages/UserProjects.jsx'; import PriceCalculationPage from '../pages/PriceCalculationPage.jsx'; +import Organization from '../components/Organization.jsx'; +import CreateEditOrganization from '../pages/CreateEditOrganization.jsx'; +import { getMyOrganizations } from '../redux/actions/organizationAction.js'; +import MyOrganization from '../pages/MyOrganizations.jsx'; const AppRoutes = () => { const { token } = useSelector(s => s.user); @@ -70,6 +74,12 @@ const AppRoutes = () => { } }, [isAuth()]); + useEffect(() => { + if (isAuth()) { + dispatch(getMyOrganizations()); + } + }, [isAuth()]); + useEffect(() => { if (isAuth()) { dispatch(getProjects()); @@ -325,6 +335,42 @@ const AppRoutes = () => { } /> + + + + } + /> + + + + + } + /> + + + + + } + /> + + + + + } + /> + {/*Add new routes here*/} } /> From 361ace0fa330a17edeeef84d93c555342043171b Mon Sep 17 00:00:00 2001 From: medetkazizov Date: Tue, 23 Jul 2024 17:52:18 +0600 Subject: [PATCH 05/39] rework of user page with organization added page for organizations, and connected backend to get edit and create organization --- .../IdentitySetting/IdentitySetting.jsx | 22 +++++-------------- src/components/User-info.jsx | 4 ++-- src/pages/MyOrganizations.jsx | 4 ++-- 3 files changed, 10 insertions(+), 20 deletions(-) diff --git a/src/components/IdentitySetting/IdentitySetting.jsx b/src/components/IdentitySetting/IdentitySetting.jsx index 53206e93..c3f78c2a 100644 --- a/src/components/IdentitySetting/IdentitySetting.jsx +++ b/src/components/IdentitySetting/IdentitySetting.jsx @@ -138,13 +138,8 @@ const IdentitySetting = () => { {linkedAccounts ?.filter(el => el.name.toLowerCase() === 'github') ?.map(el => ( - <> - + + { > - + ))} { {linkedAccounts ?.filter(el => el.name.toLowerCase() === 'linkedin') ?.map(el => ( - <> - + + { > - + ))} { {organizations?.map(org => { if (org.avatar) { return ( - + { ); } else { return ( - + { > {own?.map(org => { return ( - + { > {organizations?.map(org => { return ( - + Date: Tue, 23 Jul 2024 19:06:19 +0600 Subject: [PATCH 06/39] fixes of dispatch in create organization --- src/components/User-info.jsx | 22 +++++++++--- .../CreateEditOrganizationForm.jsx | 36 +++++-------------- src/pages/CreateEditOrganization.jsx | 10 ++++-- src/pages/MyOrganizations.jsx | 14 ++++++-- src/redux/reducers/organizationReducer.js | 6 +++- 5 files changed, 50 insertions(+), 38 deletions(-) diff --git a/src/components/User-info.jsx b/src/components/User-info.jsx index e4568291..ba7f72b1 100644 --- a/src/components/User-info.jsx +++ b/src/components/User-info.jsx @@ -127,16 +127,22 @@ const UserInfo = ({ role, linkId }) => { Organization - + {organizations?.map(org => { if (org.avatar) { return ( @@ -148,10 +154,16 @@ const UserInfo = ({ role, linkId }) => { {org.name} diff --git a/src/components/forms/create-edit-organization-form/CreateEditOrganizationForm.jsx b/src/components/forms/create-edit-organization-form/CreateEditOrganizationForm.jsx index 9df1b41a..75975d66 100644 --- a/src/components/forms/create-edit-organization-form/CreateEditOrganizationForm.jsx +++ b/src/components/forms/create-edit-organization-form/CreateEditOrganizationForm.jsx @@ -36,7 +36,7 @@ const GoBack = ({ role, newLinkId }) => { ); }; -const CreateEditOrganizationForm = ({ role, organization }) => { +const CreateEditOrganizationForm = ({ role, needLoad, organization }) => { const matchSm = useMediaQuery(theme.breakpoints.down('sm')); const matchXs = useMediaQuery(theme.breakpoints.down('xs')); const dispatch = useDispatch(); @@ -46,7 +46,7 @@ const CreateEditOrganizationForm = ({ role, organization }) => { const navigate = useNavigate(); const [isDirty, setIsDirty] = useState(false); - if (!organization.id) { + if (!organization.id && needLoad) { return ; } else { return ( @@ -71,10 +71,14 @@ const CreateEditOrganizationForm = ({ role, organization }) => { validateOnChange={false} onSubmit={values => { setIsDirty(false); - if (values.id) { - dispatch(updateOrganization(values, navigateTo)); + if (!values.id) { + dispatch( + createOrganization(values, `/${user.current_role[0]}/${user.id}`), + ); } else { - dispatch(createOrganization(values, navigateTo)); + dispatch( + updateOrganization(values, `/${user.current_role[0]}/${user.id}`), + ); } }} > @@ -204,28 +208,6 @@ const CreateEditOrganizationForm = ({ role, organization }) => { multiline rows={3} /> - {role !== CUSTOMER && ( - - - Price per line of code - - { - const value = Array.isArray(newValue) - ? newValue - : [newValue, newValue]; - setFieldValue('price_range.from', value[0]); - setFieldValue('price_range.to', value[1]); - }} - /> - - )} diff --git a/src/pages/CreateEditOrganization.jsx b/src/pages/CreateEditOrganization.jsx index ea267b38..c7edb446 100644 --- a/src/pages/CreateEditOrganization.jsx +++ b/src/pages/CreateEditOrganization.jsx @@ -21,7 +21,9 @@ const CreateEditOrganization = () => { dispatch(getOrganizationById(id)); } return () => { - dispatch(clearOrganization()); + if (id) { + dispatch(clearOrganization()); + } }; }, [id]); @@ -33,7 +35,11 @@ const CreateEditOrganization = () => { /> - + ); diff --git a/src/pages/MyOrganizations.jsx b/src/pages/MyOrganizations.jsx index b125f1aa..132cb6f1 100644 --- a/src/pages/MyOrganizations.jsx +++ b/src/pages/MyOrganizations.jsx @@ -66,7 +66,7 @@ const MyOrganization = () => { position: 'absolute', minWidth: 'unset', }} - onClick={() => navigate(`/c/${user.id}`)} + onClick={() => navigate(`/${user.current_role[0]}/${user.id}`)} > { @@ -124,7 +128,11 @@ const MyOrganization = () => { diff --git a/src/redux/reducers/organizationReducer.js b/src/redux/reducers/organizationReducer.js index 150d5b51..7c5f46a0 100644 --- a/src/redux/reducers/organizationReducer.js +++ b/src/redux/reducers/organizationReducer.js @@ -25,7 +25,8 @@ export const organizationReducer = (state = initialState, action) => { case CREATE_ORGANIZATION: return { ...state, - organization: [...state.organization, action.payload], + organization: action.payload, + organizations: [...state.organizations, action.payload], own: [...state.own, action.payload], }; case UPDATE_ORGANIZATION: @@ -35,6 +36,9 @@ export const organizationReducer = (state = initialState, action) => { own: state.own.map(el => el.id === action.payload.id ? action.payload : el, ), + organizations: state.organizations.map(el => + el.id === action.payload.id ? action.payload : el, + ), }; case GET_ORGANIZATION_BY_ID: return { From b63d45ed40c570171514d2200bf5f9f7691056ca Mon Sep 17 00:00:00 2001 From: medetkazizov Date: Fri, 26 Jul 2024 14:11:17 +0600 Subject: [PATCH 07/39] added option to add user in org --- .../AddUserToOrganization.jsx | 8 ++ src/components/AuditorSearchModal.jsx | 78 ++++++++++++++++++- src/components/Organization.jsx | 52 +++++++++---- 3 files changed, 123 insertions(+), 15 deletions(-) create mode 100644 src/components/AddUserToOrganization/AddUserToOrganization.jsx diff --git a/src/components/AddUserToOrganization/AddUserToOrganization.jsx b/src/components/AddUserToOrganization/AddUserToOrganization.jsx new file mode 100644 index 00000000..20fb2286 --- /dev/null +++ b/src/components/AddUserToOrganization/AddUserToOrganization.jsx @@ -0,0 +1,8 @@ +import React from 'react'; +import Box from '@mui/material/Box'; + +const AddUserToOrganization = () => { + return ; +}; +// +export default AddUserToOrganization; diff --git a/src/components/AuditorSearchModal.jsx b/src/components/AuditorSearchModal.jsx index b7326065..6bf13008 100644 --- a/src/components/AuditorSearchModal.jsx +++ b/src/components/AuditorSearchModal.jsx @@ -9,7 +9,14 @@ import InputAdornment from '@mui/material/InputAdornment'; import SearchIcon from '@mui/icons-material/Search'; import Autocomplete from '@mui/material/Autocomplete'; import { useEffect, useState } from 'react'; -import { Paper, Slider, Typography } from '@mui/material'; +import { + Avatar, + Checkbox, + FormControlLabel, + Paper, + Slider, + Typography, +} from '@mui/material'; import AuditorSearchListBox from './custom/AuditorSearchListBox.jsx'; import IconButton from '@mui/material/IconButton'; import { ArrowBack } from '@mui/icons-material'; @@ -28,6 +35,7 @@ import { useParams } from 'react-router-dom'; import { addTestsLabel } from '../lib/helper.js'; import CustomSnackbar from './custom/CustomSnackbar.jsx'; import PriceCalculation from './PriceCalculation.jsx'; +import { ASSET_URL } from '../services/urls.js'; export default function AuditorSearchModal({ open, @@ -35,6 +43,7 @@ export default function AuditorSearchModal({ handleSubmit, setState, setError, + invite, }) { const navigate = useNavigate(); const dispatch = useDispatch(); @@ -43,6 +52,7 @@ export default function AuditorSearchModal({ const projectReducer = useSelector(state => state.project); const customerReducer = useSelector(state => state.customer); const [selectedAuditor, setSelectedAuditor] = useState({}); + const organization = useSelector(s => s.organization.organization); const [openDrop, setOpenDrop] = useState(false); const [mode, setMode] = useState('search'); @@ -60,7 +70,15 @@ export default function AuditorSearchModal({ const handleOptionChange = option => { setSelectedAuditor(option); - setMode('offer'); + if (invite) { + setMode('invite'); + } else { + setMode('offer'); + } + }; + + const handleInviteUser = () => { + console.log({ ...selectedAuditor }); }; const handleSearch = async () => { @@ -291,6 +309,62 @@ export default function AuditorSearchModal({ )} + {mode === 'invite' && ( + + + + Current organization + + + + {organization.name} + + + Rules for the selected user in the organization + + + } + label="Representative" + labelPlacement="top" + /> + } + label="Editor" + labelPlacement="top" + /> + + + + + )} ); } diff --git a/src/components/Organization.jsx b/src/components/Organization.jsx index 812f10b1..27f49197 100644 --- a/src/components/Organization.jsx +++ b/src/components/Organization.jsx @@ -1,4 +1,4 @@ -import React, { useMemo, useEffect } from 'react'; +import React, { useMemo, useEffect, useState } from 'react'; import { Avatar, Box, @@ -40,6 +40,7 @@ import { getOrganizationById, } from '../redux/actions/organizationAction.js'; import InfoCard from './custom/info-card.jsx'; +import AuditorSearchModal from './AuditorSearchModal.jsx'; const Organization = () => { const { id: linkId } = useParams(); @@ -49,7 +50,8 @@ const Organization = () => { const matchXs = useMediaQuery(theme.breakpoints.down('xs')); const matchXxs = useMediaQuery(theme.breakpoints.down(590)); const organization = useSelector(s => s.organization.organization); - + const [showAddUser, setShowAddUser] = useState(false); + // const { customer, error: customerError, @@ -73,6 +75,10 @@ const Organization = () => { navigate(`/edit-organization/${linkId}`); }; + const handleAddUser = () => { + setShowAddUser(!showAddUser); + }; + const data = useMemo(() => { if (role === AUDITOR) { return auditor; @@ -119,6 +125,15 @@ const Organization = () => { color={role === CUSTOMER ? 'primary' : 'secondary'} /> + setShowAddUser(false)} + handleSubmit={() => console.log(123)} + setState={() => console.log(444)} + setError={() => console.log('error')} + /> {/* { } })} - + + + organization.owner.user_id === user.id && + value.user_id !== user.id && ( + + ) } disablePadding > @@ -242,7 +265,11 @@ const Organization = () => { { } })} - - - - {/**/} - + {isAuth() && isMyOrg && ( + + + + {/**/} + + )} + {inviteMe && !isMyOrg && ( + + + + + )} + { + dispatch(deleteInvites(organization.id, user.id)); + setIsOpenConfirm(false); + }} + handleDisagree={() => setIsOpenConfirm(false)} + isOpen={openConfirm} + /> diff --git a/src/components/OrganizationList/OrganizationList.jsx b/src/components/OrganizationList/OrganizationList.jsx new file mode 100644 index 00000000..50529ef3 --- /dev/null +++ b/src/components/OrganizationList/OrganizationList.jsx @@ -0,0 +1,58 @@ +import React from 'react'; +import { Link } from 'react-router-dom'; +import { Avatar, Box, Tooltip } from '@mui/material'; +import { ASSET_URL } from '../../services/urls.js'; +import { CUSTOMER } from '../../redux/actions/types.js'; +import theme from '../../styles/themes.js'; +import { useSelector } from 'react-redux'; + +const OrganizationList = ({ organizations }) => { + const user = useSelector(s => s.user.user); + return ( + + {organizations?.map(org => { + if (org.avatar) { + return ( + + + + + + ); + } else { + return ( + + + + {org.name} + + + + ); + } + })} + + ); +}; + +export default OrganizationList; diff --git a/src/components/User-info.jsx b/src/components/User-info.jsx index b9e84330..9f5b414f 100644 --- a/src/components/User-info.jsx +++ b/src/components/User-info.jsx @@ -25,6 +25,7 @@ import WalletConnectIcon from './icons/WalletConnectIcon.jsx'; import { clearUserMessages } from '../redux/actions/userAction.js'; import CustomSnackbar from './custom/CustomSnackbar.jsx'; import Headings from '../router/Headings.jsx'; +import OrganizationList from './OrganizationList/OrganizationList.jsx'; const UserInfo = ({ role, linkId }) => { const dispatch = useDispatch(); @@ -32,6 +33,7 @@ const UserInfo = ({ role, linkId }) => { const matchXs = useMediaQuery(theme.breakpoints.down('xs')); const matchXxs = useMediaQuery(theme.breakpoints.down(850)); const organizations = useSelector(s => s.organization.organizations); + const invites = useSelector(s => s.organization.invites); const { customer, @@ -125,56 +127,16 @@ const UserInfo = ({ role, linkId }) => { }, }} > + {!!invites.length && ( + <> + Invites + + + )} Organization - - {organizations?.map(org => { - if (org.avatar) { - return ( - - - - - - ); - } else { - return ( - - - - {org.name} - - - - ); - } - })} - + )} diff --git a/src/components/audit-request-info.jsx b/src/components/audit-request-info.jsx index 9948c409..3758df18 100644 --- a/src/components/audit-request-info.jsx +++ b/src/components/audit-request-info.jsx @@ -13,6 +13,12 @@ import { Modal, Tooltip, Divider, + Popover, + Avatar, + ListItemAvatar, + ListItem, + ListItemText, + List, } from '@mui/material'; import { CustomCard } from './custom/Card.jsx'; import theme from '../styles/themes.js'; @@ -40,6 +46,8 @@ import EditDescription from './EditDescription/index.jsx'; import DescriptionHistory from './DescriptionHistory/index.jsx'; import EditTags from './EditDescription/EditTags.jsx'; import EditPrice from './EditDescription/EditPrice.jsx'; +import { ASSET_URL } from '../services/urls.js'; +import ListItemButton from '@mui/material/ListItemButton'; const AuditRequestInfo = ({ project, @@ -54,10 +62,13 @@ const AuditRequestInfo = ({ const navigate = useNavigate(); const matchXs = useMediaQuery(theme.breakpoints.down('xs')); const [open, setOpen] = useState(false); + const [anchorEl, setAnchorEl] = useState(null); const [confirmDeclineOpen, setConfirmDeclineOpen] = useState(false); const [showAcceptButton, setShowAcceptButton] = useState(true); - + const [visible, setVisible] = useState(false); + const organizations = useSelector(state => state.organization.organizations); const { auditor } = useSelector(s => s.auditor); + const [auditorData, setAuditorData] = useState({}); const { auditRequest, auditRequests, successMessage } = useSelector( s => s.audits, ); @@ -68,9 +79,36 @@ const AuditRequestInfo = ({ const { successMessage: auditSuccessMessage, error: auditError } = useSelector(s => s.audits); - const handleOpen = () => { + const handleClick = event => { + setAnchorEl(event.currentTarget); + setVisible(true); + }; + + const handleCloseAnchor = () => { + setAnchorEl(null); + }; + + const anchorOrigin = { + vertical: visible ? 'bottom' : 'top', + horizontal: 'left', + }; + + const transformOrigin = { + vertical: visible ? 'top' : 'bottom', + horizontal: 'left', + }; + + const openAnchor = Boolean(anchorEl); + const id = openAnchor ? 'simple-popover' : undefined; + + const handleOpen = event => { if (user.current_role === AUDITOR && isAuth() && auditor?.first_name) { - setOpen(true); + if (!organizations.length) { + setAuditorData(auditor); + setOpen(true); + } else { + handleClick(event); + } } else if ( user.current_role !== AUDITOR && isAuth() && @@ -78,6 +116,7 @@ const AuditRequestInfo = ({ ) { dispatch(changeRolePublicAuditorNoRedirect(AUDITOR, user.id, auditor)); handleError(); + setAuditorData(auditor); setOpen(true); } else if ( !auditor?.first_name && @@ -92,12 +131,18 @@ const AuditRequestInfo = ({ ) { dispatch(changeRolePublicAuditor(AUDITOR, user.id, auditor)); handleError(); + setAuditorData(auditor); setOpen(true); } else { navigate('/sign-in'); } }; + const handleChose = auditor => { + setAuditorData(auditor); + setOpen(true); + }; + // const handleClose = () => { setOpen(false); }; @@ -185,6 +230,28 @@ const AuditRequestInfo = ({ {project?.name || project?.project_name} + {project?.auditor_organization?.id && ( + + + Organization: + + + {project?.auditor_organization.name} + + + )} @@ -480,6 +547,61 @@ const AuditRequestInfo = ({ > Make offer + + + handleChose(auditor)}> + + + + + + + + {organizations.map(org => ( + member.user_id === user.id) + .access_level.some(level => level === 'Editor') + } + onClick={() => { + if ( + org.members + .find(member => member.user_id === user.id) + .access_level.some(level => level === 'Editor') + ) { + handleChose(org); + } + }} + > + + + + + + + + ))} + + {showAcceptButton && auditRequest && !isModal && @@ -512,7 +634,7 @@ const AuditRequestInfo = ({ disableScrollLock > { const role = useSelector(s => s.user.user.current_role); @@ -116,6 +117,11 @@ const CustomMessage = ({ message }) => { {!!organizations.length && ( @@ -141,7 +141,7 @@ export const UserMenu = ({ open, handleClose, anchor, userAvatar, pages }) => { onClick={handleMyOrganizationClick} {...addTestsLabel('header_my-account')} > - My Organizations + My organizations )} diff --git a/src/pages/CreateEditOrganization.jsx b/src/pages/CreateEditOrganization.jsx index 9000a13e..191f17b4 100644 --- a/src/pages/CreateEditOrganization.jsx +++ b/src/pages/CreateEditOrganization.jsx @@ -44,7 +44,9 @@ const CreateEditOrganization = () => { newLinkId={newLinkId} organization={organization} /> - + {organization.id && ( + + )} ); diff --git a/src/pages/MyOrganizations.jsx b/src/pages/MyOrganizations.jsx index 954a09c8..23ccf1db 100644 --- a/src/pages/MyOrganizations.jsx +++ b/src/pages/MyOrganizations.jsx @@ -58,7 +58,7 @@ const MyOrganization = () => { return ( - + - ) - } - disablePadding - > - - - - - - - + ); })} diff --git a/src/components/UserListItem/UserLIstItem.jsx b/src/components/UserListItem/UserLIstItem.jsx new file mode 100644 index 00000000..51fb6cbd --- /dev/null +++ b/src/components/UserListItem/UserLIstItem.jsx @@ -0,0 +1,225 @@ +import React, { useState } from 'react'; +import { + Avatar, + Box, + Button, + Checkbox, + Chip, + FormControlLabel, + ListItem, + ListItemAvatar, + ListItemText, + Modal, + Typography, +} from '@mui/material'; +import EditIcon from '@mui/icons-material/Edit.js'; +import { + changeAccessLevel, + deleteUserFromOrganization, +} from '../../redux/actions/organizationAction.js'; +import DeleteForeverRoundedIcon from '@mui/icons-material/DeleteForeverRounded.js'; +import ListItemButton from '@mui/material/ListItemButton'; +import { ASSET_URL } from '../../services/urls.js'; +import { useDispatch, useSelector } from 'react-redux'; +import ConfirmModal from '../modal/ConfirmModal.jsx'; + +const UserLIstItem = ({ value, labelId, organization }) => { + const user = useSelector(s => s.user.user); + const [isOpen, setIsOpen] = useState(false); + const dispatch = useDispatch(); + const [isOpenConfirm, setIsOpenConfirm] = useState(false); + const [rulesOfMember, setRulesOfMember] = useState({ + Representative: value.access_level.includes('Representative'), + Editor: value.access_level.includes('Editor'), + }); + + const handleChangeOwner = () => { + dispatch( + changeAccessLevel( + organization.id, + value.user_id, + ['Owner', 'Representative', 'Editor'], + organization.link_id, + ), + ); + setIsOpenConfirm(false); + setIsOpen(false); + }; + + const handleClose = () => { + setRulesOfMember({ + Representative: value.access_level.includes('Representative'), + Editor: value.access_level.includes('Editor'), + }); + setIsOpen(false); + }; + + return ( + + + + + + + Rules of {value.username} + + + } + label="Representative" + labelPlacement="top" + checked={rulesOfMember.Representative} + onChange={e => { + setRulesOfMember({ + ...rulesOfMember, + Representative: e.target.checked, + }); + }} + /> + } + label="Editor" + checked={rulesOfMember.Editor} + labelPlacement="top" + onChange={e => { + setRulesOfMember({ + ...rulesOfMember, + Editor: e.target.checked, + }); + }} + /> + + + + + handleChangeOwner()} + handleDisagree={() => setIsOpenConfirm(false)} + isOpen={isOpenConfirm} + /> + + + + + ) + } + disablePadding + > + + + + + + {value.access_level.includes('Owner') && ( + + )} + + + ); +}; + +export default UserLIstItem; + +const modalSx = theme => ({ + position: 'absolute', + width: 400, + bgcolor: 'background.paper', + borderRadius: '8px', + boxShadow: 24, + p: 4, + top: '50%', + left: '50%', + transform: 'translate(-50%, -50%)', + [theme.breakpoints.down(500)]: { + width: '310px', + p: 2, + }, +}); + +const userActionSx = theme => ({ + minWidth: 'unset', + padding: '5px', + mx: '5px', +}); diff --git a/src/components/forms/create-edit-organization-form/CreateEditOrganizationForm.jsx b/src/components/forms/create-edit-organization-form/CreateEditOrganizationForm.jsx index 00c98b90..47d8c9f8 100644 --- a/src/components/forms/create-edit-organization-form/CreateEditOrganizationForm.jsx +++ b/src/components/forms/create-edit-organization-form/CreateEditOrganizationForm.jsx @@ -61,7 +61,11 @@ const CreateEditOrganizationForm = ({ const [isDirty, setIsDirty] = useState(false); if (!organization.id && needLoad) { - return ; + return ( + + + + ); } else { return ( @@ -268,6 +273,15 @@ const CreateEditOrganizationForm = ({ } label="Auditor" /> + {user.current_role.slice(0, 1).toUpperCase() + + user.current_role.slice(1) !== + values.organization_type && ( + + The role you've assigned to the organization is + different from yours. As a result, your role will be + changed when the creation process is finished. + + )} diff --git a/src/pages/CreateEditOrganization.jsx b/src/pages/CreateEditOrganization.jsx index 191f17b4..994abdde 100644 --- a/src/pages/CreateEditOrganization.jsx +++ b/src/pages/CreateEditOrganization.jsx @@ -10,12 +10,16 @@ import { getOrganizationById, } from '../redux/actions/organizationAction.js'; import ChangeLinkId from '../components/forms/change-link-id/index.jsx'; +import NotFound from './Not-Found.jsx'; +import { Box } from '@mui/material'; +import Loader from '../components/Loader.jsx'; const CreateEditOrganization = () => { const { id } = useParams(); const dispatch = useDispatch(); const role = useSelector(s => s.user.user.current_role); const organization = useSelector(s => s.organization.organization); + const notFound = useSelector(s => s.organization.notFound); const [newLinkId, setNewLinkId] = useState(null); const [showChangeLinkId, setShowChangeLinkId] = useState(false); @@ -30,26 +34,30 @@ const CreateEditOrganization = () => { }; }, [id]); - return ( - - - - - + - {organization.id && ( - - )} - - - ); + + + + {organization.id && ( + + )} + + + ); + } else { + return ; + } }; export default CreateEditOrganization; diff --git a/src/pages/MyOrganizations.jsx b/src/pages/MyOrganizations.jsx index 23ccf1db..afbd4335 100644 --- a/src/pages/MyOrganizations.jsx +++ b/src/pages/MyOrganizations.jsx @@ -124,7 +124,7 @@ const MyOrganization = () => { > {organizations?.map(org => { return ( - + { }) .then(({ data }) => { dispatch({ type: GET_ORGANIZATION_BY_ID, payload: data }); - }); + }) + .catch(e => dispatch({ type: NOT_FOUND_ORGANIZATION })); }; }; @@ -110,8 +112,23 @@ export const addUserInOrganization = (orgLinkId, data, id) => { }); }; }; -// -export const deleteUserFromOrganization = (orgId, userId) => { + +export const changeAccessLevel = (org_id, user_id, data, orgLinkId) => { + const token = Cookies.get('token'); + return dispatch => { + axios + .patch(`${API_URL}/organization/${org_id}/members/${user_id}`, data, { + headers: { + Authorization: `Bearer ${token}`, + }, + }) + .then(({ data }) => { + dispatch(getOrganizationById(orgLinkId)); + }); + }; +}; + +export const deleteUserFromOrganization = (orgId, userId, linkId) => { return dispatch => { const token = Cookies.get('token'); axios @@ -121,7 +138,7 @@ export const deleteUserFromOrganization = (orgId, userId) => { }, }) .then(({ data }) => { - dispatch(getOrganizationById(orgId)); + dispatch(getOrganizationById(linkId)); }); }; }; @@ -155,7 +172,6 @@ export const getAuditRequests = org_id => { }, }) .then(({ data }) => { - console.log(data); dispatch({ type: GET_ORGANIZATION_AUDIT_REQUESTS, payload: data }); }); }; diff --git a/src/redux/actions/types.js b/src/redux/actions/types.js index b2d5a02a..56cb0174 100644 --- a/src/redux/actions/types.js +++ b/src/redux/actions/types.js @@ -158,6 +158,7 @@ export const ADD_MEMBER_IN_ORGANIZATION = 'ADD_MEMBER_IN_ORGANIZATION'; export const EDIT_AUDIT_CUSTOMER = 'EDIT_AUDIT_CUSTOMER'; export const DELETE_INVITES = 'DELETE_INVITES'; export const ACCEPT_INVITE = 'ACCEPT_INVITE'; +export const NOT_FOUND_ORGANIZATION = 'NOT_FOUND_ORGANIZATION'; export const GET_ORGANIZATION_AUDIT_REQUESTS = 'GET_ORGANIZATION_AUDIT_REQUESTS'; export const GET_AUDIT_REQUEST_HISTORY = 'GET_AUDIT_REQUEST_HISTORY'; diff --git a/src/redux/reducers/organizationReducer.js b/src/redux/reducers/organizationReducer.js index 5f96c974..748316fc 100644 --- a/src/redux/reducers/organizationReducer.js +++ b/src/redux/reducers/organizationReducer.js @@ -6,6 +6,7 @@ import { CLEAR_ORGANIZATION, DELETE_INVITES, ACCEPT_INVITE, + NOT_FOUND_ORGANIZATION, } from '../actions/types.js'; const initialState = { @@ -14,6 +15,7 @@ const initialState = { includeMe: [], organization: {}, invites: [], + notFound: false, }; export const organizationReducer = (state = initialState, action) => { @@ -33,6 +35,11 @@ export const organizationReducer = (state = initialState, action) => { organizations: [...state.organizations, action.payload], own: [...state.own, action.payload], }; + case NOT_FOUND_ORGANIZATION: + return { + ...state, + notFound: true, + }; case UPDATE_ORGANIZATION: return { ...state, From 1c955f68947f8b574c0d74f48c10b70485aa47c6 Mon Sep 17 00:00:00 2001 From: medetkazizov Date: Thu, 19 Sep 2024 16:09:34 +0600 Subject: [PATCH 11/39] changed view of Organization card and added snackbar when invite user --- src/components/AuditorSearchModal.jsx | 30 +- src/components/Organization.jsx | 8 +- src/components/header/UserMenu.jsx | 22 +- src/pages/MyOrganizations.jsx | 334 +++++++++++++++------- src/redux/actions/organizationAction.js | 1 + src/redux/reducers/organizationReducer.js | 13 + 6 files changed, 267 insertions(+), 141 deletions(-) diff --git a/src/components/AuditorSearchModal.jsx b/src/components/AuditorSearchModal.jsx index 7b17c2c7..14f0692f 100644 --- a/src/components/AuditorSearchModal.jsx +++ b/src/components/AuditorSearchModal.jsx @@ -97,16 +97,14 @@ export default function AuditorSearchModal({ const data = [ { user_id: selectedAuditor.user_id, - access_level: [ - rulesOfMember.representative ? 'Representative' : '', - rulesOfMember.editor ? 'Editor' : '', - ].filter(rule => rule), + access_level: rulesOfMember.editor ? 'Editor' : '', }, ]; dispatch( addUserInOrganization(organization.link_id, data, organization.id), ); setMode('search'); + setQuery(''); handleClose(); }; @@ -368,18 +366,18 @@ export default function AuditorSearchModal({ gap: '20px', }} > - } - label="Representative" - labelPlacement="top" - onChange={e => { - setRulesOfMember({ - ...rulesOfMember, - representative: e.target.checked, - }); - }} - /> + {/*}*/} + {/* label="Representative"*/} + {/* labelPlacement="top"*/} + {/* onChange={e => {*/} + {/* setRulesOfMember({*/} + {/* ...rulesOfMember,*/} + {/* representative: e.target.checked,*/} + {/* });*/} + {/* }}*/} + {/*/>*/} } diff --git a/src/components/Organization.jsx b/src/components/Organization.jsx index d0df9a71..a60124b6 100644 --- a/src/components/Organization.jsx +++ b/src/components/Organization.jsx @@ -65,8 +65,8 @@ const Organization = ({ linkId }) => { const { customer, error: customerError, - success: customerSuccess, - } = useSelector(s => s.customer); + successMessage: successMessage, + } = useSelector(s => s.organization); const { auditor, error: auditorError, @@ -163,14 +163,14 @@ const Organization = ({ linkId }) => { !!error || !!customerError || !!auditorError || - !!customerSuccess || + !!successMessage || !!auditorSuccess } text={ error || customerError || auditorError || - customerSuccess || + successMessage || auditorSuccess } severity={ diff --git a/src/components/header/UserMenu.jsx b/src/components/header/UserMenu.jsx index eba78f39..1b3f6442 100644 --- a/src/components/header/UserMenu.jsx +++ b/src/components/header/UserMenu.jsx @@ -134,17 +134,17 @@ export const UserMenu = ({ open, handleClose, anchor, userAvatar, pages }) => { My account - {!!organizations.length && ( - - - - )} + {/*{!!organizations.length && (*/} + + + + {/*)}*/} {matchSm && pages?.map(el => el.menuOptions diff --git a/src/pages/MyOrganizations.jsx b/src/pages/MyOrganizations.jsx index afbd4335..3ce59e2a 100644 --- a/src/pages/MyOrganizations.jsx +++ b/src/pages/MyOrganizations.jsx @@ -6,6 +6,11 @@ import { Typography, useMediaQuery, Tooltip, + Grid, + Card, + CardMedia, + CardContent, + CardActions, } from '@mui/material'; import ArrowBackIcon from '@mui/icons-material/ArrowBack.js'; import theme from '../styles/themes.js'; @@ -40,123 +45,232 @@ const MyOrganization = () => { } = useSelector(s => s.auditor); const { user, error } = useSelector(s => s.user); - if (!organizations.length && !own.length) { - return ( - - - - - - - ); - } else { - return ( - - - - - - - {!!own.length && ( - - My organizations - - {own?.map(org => { - return ( - - - - - - {org.name} - - - - - ); - })} + // if (!organizations.length && !own.length) { + // return ( + // + // + // + // + // + // + // ); + // } else { + return ( + + + + + + + {!organizations.length || !own.length ? ( + + + + ) : ( + <> + {!!own.length && ( + + My organizations + + {own?.map(org => { + return ( + + + + {org.avatar ? ( + + ) : ( + + + {org.name.slice(0, 3)} + + + )} + + {org.name} + + + + + ); + })} + - - )} - {!!organizations.length && ( - - Organizations - - {organizations?.map(org => { - return ( - - - - - - {org.name} - - - - - ); - })} + )} + {!!organizations.length && ( + + Organizations + + {organizations?.map(org => { + return ( + + + + {org.avatar ? ( + + ) : ( + + + {org.name.slice(0, 3)} + + + )} + + {org.name} + + + + + ); + })} + - - )} - + )} + + )} + - - - ); - } + + + + ); + // } }; export default MyOrganization; +const buttonSx = theme => ({ + margin: '0 auto', + display: 'block', + color: theme.palette.background.default, + textTransform: 'capitalize', + fontWeight: 600, + fontSize: '18px', + // padding: '9px 50px', + width: '234px', + borderRadius: '10px', + [theme.breakpoints.down('xs')]: { + padding: '9px 10px', + }, +}); + +const gridItemSx = theme => ({ + width: '20%', + [theme.breakpoints.down('md')]: { + width: '25%', + }, + [theme.breakpoints.down('sm')]: { + width: '33.33%', + }, + [theme.breakpoints.down('xs')]: { + width: '50%', + paddingTop: '15px', + paddingLeft: '15px', + }, +}); + const wrapper = theme => ({ display: 'flex', flexDirection: 'column', @@ -177,7 +291,7 @@ const innerWrapper = theme => ({ minHeight: '520px', display: 'flex', flexDirection: 'column', - padding: '60px 40px 40px', + padding: '40px 40px 40px', gap: '30px', justifyContent: 'space-between', [theme.breakpoints.down('sm')]: { diff --git a/src/redux/actions/organizationAction.js b/src/redux/actions/organizationAction.js index af65c6a9..a37ecdea 100644 --- a/src/redux/actions/organizationAction.js +++ b/src/redux/actions/organizationAction.js @@ -108,6 +108,7 @@ export const addUserInOrganization = (orgLinkId, data, id) => { }, }) .then(({ data }) => { + dispatch({ type: ADD_MEMBER_IN_ORGANIZATION }); dispatch(getOrganizationById(orgLinkId)); }); }; diff --git a/src/redux/reducers/organizationReducer.js b/src/redux/reducers/organizationReducer.js index 748316fc..de7ffeac 100644 --- a/src/redux/reducers/organizationReducer.js +++ b/src/redux/reducers/organizationReducer.js @@ -7,6 +7,8 @@ import { DELETE_INVITES, ACCEPT_INVITE, NOT_FOUND_ORGANIZATION, + ADD_MEMBER_IN_ORGANIZATION, + CLEAR_MESSAGES, } from '../actions/types.js'; const initialState = { @@ -16,6 +18,7 @@ const initialState = { organization: {}, invites: [], notFound: false, + successMessage: '', }; export const organizationReducer = (state = initialState, action) => { @@ -28,6 +31,16 @@ export const organizationReducer = (state = initialState, action) => { includeMe: action.payload.member, invites: action.payload.invites, }; + case ADD_MEMBER_IN_ORGANIZATION: + return { + ...state, + successMessage: 'User successfully invited', + }; + case CLEAR_MESSAGES: + return { + ...state, + successMessage: '', + }; case CREATE_ORGANIZATION: return { ...state, From 12adbc1ba834a38a938a72f269119371efd8fbfc Mon Sep 17 00:00:00 2001 From: medetkazizov Date: Fri, 20 Sep 2024 15:08:33 +0600 Subject: [PATCH 12/39] added radio button tool in organization form --- .../CreateEditOrganizationForm.jsx | 41 ++++++++++++++-- src/redux/actions/userAction.js | 49 +++++++++++++++++++ 2 files changed, 85 insertions(+), 5 deletions(-) diff --git a/src/components/forms/create-edit-organization-form/CreateEditOrganizationForm.jsx b/src/components/forms/create-edit-organization-form/CreateEditOrganizationForm.jsx index 47d8c9f8..cb67b89e 100644 --- a/src/components/forms/create-edit-organization-form/CreateEditOrganizationForm.jsx +++ b/src/components/forms/create-edit-organization-form/CreateEditOrganizationForm.jsx @@ -28,6 +28,10 @@ import RadioGroup from '@mui/material/RadioGroup'; import FormControlLabel from '@mui/material/FormControlLabel'; import FormControl from '@mui/material/FormControl'; import FormLabel from '@mui/material/FormLabel'; +import { + changeRoleCreateOrganization, + changeRolePublicAuditorNoRedirect, +} from '../../../redux/actions/userAction.js'; const GoBack = ({ role, newLinkId }) => { const navigate = useNavigate(); @@ -88,17 +92,37 @@ const CreateEditOrganizationForm = ({ validationSchema={EditOrganizationSchema} validateOnBlur={false} validateOnChange={false} - onSubmit={values => { + onSubmit={(values, { resetForm }) => { setIsDirty(false); if (!values.id) { - dispatch( - createOrganization(values, `/${user.current_role[0]}/${user.id}`), - ); + if ( + organization.organization_type?.toLowerCase() !== + user.current_role?.toLowerCase() + ) { + dispatch( + changeRoleCreateOrganization( + user.current_role?.toLowerCase() !== AUDITOR.toLowerCase() + ? AUDITOR + : CUSTOMER, + user.id, + values, + `/${user.current_role[0]}/${user.id}`, + ), + ); + } else { + dispatch( + createOrganization( + values, + `/${user.current_role[0]}/${user.id}`, + ), + ); + } } else { dispatch( updateOrganization(values, `/${user.current_role[0]}/${user.id}`), ); } + resetForm(); }} > {({ handleSubmit, values, setFieldValue, dirty }) => { @@ -276,7 +300,7 @@ const CreateEditOrganizationForm = ({ {user.current_role.slice(0, 1).toUpperCase() + user.current_role.slice(1) !== values.organization_type && ( - + The role you've assigned to the organization is different from yours. As a result, your role will be changed when the creation process is finished. @@ -314,6 +338,13 @@ const EditOrganizationSchema = Yup.object().shape({ // }), }); +const alertDescSx = theme => ({ + fontSize: '16px', + [theme.breakpoints.down('md')]: { + fontSize: '12px', + }, +}); + const backBtnSx = theme => ({ position: 'absolute', left: '-70px', diff --git a/src/redux/actions/userAction.js b/src/redux/actions/userAction.js index 092b5cc9..27c1dbb9 100644 --- a/src/redux/actions/userAction.js +++ b/src/redux/actions/userAction.js @@ -31,6 +31,7 @@ import { CLEAR_MESSAGES, AUDITOR, CUSTOMER, + CREATE_ORGANIZATION, } from './types.js'; import { savePublicReport } from './auditAction.js'; @@ -505,6 +506,54 @@ export const changeRolePublicAuditorNoRedirect = (role, id) => { }; }; +export const changeRoleCreateOrganization = (role, id, value, navigateTo) => { + return dispatch => { + axios + .patch( + `${API_URL}/user/${id}`, + { current_role: role }, + { + headers: { + Authorization: 'Bearer ' + Cookies.get('token'), + 'Content-Type': 'application/json', + }, + }, + ) + .then(({ data }) => { + dispatch({ type: SELECT_ROLE, payload: data }); + localStorage.setItem('user', JSON.stringify(data)); + const token = Cookies.get('token'); + if (data?.name) { + console.log(data); + console.log(value); + axios + .post(`${API_URL}/organization`, value, { + headers: { + Authorization: `Bearer ${token}`, + }, + }) + .then(({ data: orgData }) => { + console.log(orgData); + dispatch({ type: CREATE_ORGANIZATION, payload: orgData }); + console.log(`${data.current_role[0]}/${data.id}`, 'we'); + console.log(navigateTo); + if (navigateTo) { + history.push( + { pathname: `${data.current_role[0]}/${data.id}` }, + { some: true }, + ); + } + }) + .catch(({ response }) => { + console.log(response, 'res'); + }); + } else { + history.push({ pathname: '/edit-profile' }, { some: true }); + } + }); + }; +}; + export const changeRolePublicCustomerNoRedirect = (role, id) => { return dispatch => { axios From 7a8744efe4c4468c5f65990a42dc839affe0cda6 Mon Sep 17 00:00:00 2001 From: medetkazizov Date: Fri, 20 Sep 2024 15:10:07 +0600 Subject: [PATCH 13/39] added radio button tool in organization form --- src/redux/actions/userAction.js | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/redux/actions/userAction.js b/src/redux/actions/userAction.js index 27c1dbb9..865e46e1 100644 --- a/src/redux/actions/userAction.js +++ b/src/redux/actions/userAction.js @@ -524,8 +524,6 @@ export const changeRoleCreateOrganization = (role, id, value, navigateTo) => { localStorage.setItem('user', JSON.stringify(data)); const token = Cookies.get('token'); if (data?.name) { - console.log(data); - console.log(value); axios .post(`${API_URL}/organization`, value, { headers: { @@ -533,10 +531,7 @@ export const changeRoleCreateOrganization = (role, id, value, navigateTo) => { }, }) .then(({ data: orgData }) => { - console.log(orgData); dispatch({ type: CREATE_ORGANIZATION, payload: orgData }); - console.log(`${data.current_role[0]}/${data.id}`, 'we'); - console.log(navigateTo); if (navigateTo) { history.push( { pathname: `${data.current_role[0]}/${data.id}` }, From 09398923a2657d21c9291008e21e9d1228bfb45b Mon Sep 17 00:00:00 2001 From: medetkazizov Date: Tue, 24 Sep 2024 17:09:14 +0600 Subject: [PATCH 14/39] added radio button for access in invite user --- src/components/AuditorSearchModal.jsx | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/components/AuditorSearchModal.jsx b/src/components/AuditorSearchModal.jsx index 14f0692f..ecd57722 100644 --- a/src/components/AuditorSearchModal.jsx +++ b/src/components/AuditorSearchModal.jsx @@ -40,6 +40,7 @@ import TotalPrice from './forms/TotalPrice/TotalPrice.jsx'; import { addUserInOrganization } from '../redux/actions/organizationAction.js'; import { AUDITOR } from '../redux/actions/types.js'; import { searchCustomers } from '../redux/actions/customerAction.js'; +import Radio from '@mui/material/Radio'; export default function AuditorSearchModal({ open, @@ -58,7 +59,7 @@ export default function AuditorSearchModal({ const customerReducer = useSelector(state => state.customer); const [selectedAuditor, setSelectedAuditor] = useState({}); const organization = useSelector(s => s.organization.organization); - const [rulesOfMember, setRulesOfMember] = useState({}); + const [rulesOfMember, setRulesOfMember] = useState(false); const [openDrop, setOpenDrop] = useState(false); const [mode, setMode] = useState('search'); @@ -97,7 +98,7 @@ export default function AuditorSearchModal({ const data = [ { user_id: selectedAuditor.user_id, - access_level: rulesOfMember.editor ? 'Editor' : '', + access_level: rulesOfMember ? 'Editor' : '', }, ]; dispatch( @@ -380,15 +381,16 @@ export default function AuditorSearchModal({ {/*/>*/} } + control={ + { + setRulesOfMember(!rulesOfMember); + }} + checked={rulesOfMember} + /> + } label="Editor" labelPlacement="top" - onChange={e => { - setRulesOfMember({ - ...rulesOfMember, - editor: e.target.checked, - }); - }} /> - {!organizations.length || !own.length ? ( + {loading && !organizations.length && !own.length ? ( diff --git a/src/redux/reducers/organizationReducer.js b/src/redux/reducers/organizationReducer.js index de7ffeac..fda72067 100644 --- a/src/redux/reducers/organizationReducer.js +++ b/src/redux/reducers/organizationReducer.js @@ -19,6 +19,7 @@ const initialState = { invites: [], notFound: false, successMessage: '', + loading: true, }; export const organizationReducer = (state = initialState, action) => { @@ -30,6 +31,7 @@ export const organizationReducer = (state = initialState, action) => { own: action.payload.owner, includeMe: action.payload.member, invites: action.payload.invites, + loading: false, }; case ADD_MEMBER_IN_ORGANIZATION: return { diff --git a/src/routes/AppRoutes.jsx b/src/routes/AppRoutes.jsx index 5efd79b7..d594e929 100644 --- a/src/routes/AppRoutes.jsx +++ b/src/routes/AppRoutes.jsx @@ -88,7 +88,7 @@ const AppRoutes = () => { }, [isAuth(), currentRole]); useEffect(() => { - if (organizations.length) { + if (organizations?.length) { dispatch(getAuditRequests()); } }, [organizations]); From 24f2f5f60324453c5ce20c6f30c5c48998b7cc00 Mon Sep 17 00:00:00 2001 From: medetkazizov Date: Fri, 11 Oct 2024 14:45:29 +0600 Subject: [PATCH 20/39] logic for chat from org --- src/components/AuditorModal.jsx | 24 +-- src/components/Chat/CurrentChat.jsx | 7 +- src/components/Chat/TypeChat.jsx | 274 +++++++++++++++++++++----- src/components/audit-request-info.jsx | 83 ++++---- src/pages/ChatPage.jsx | 174 ++++++++++------ src/pages/MyOrganizations.jsx | 2 +- src/redux/actions/chatActions.js | 26 ++- src/redux/actions/types.js | 1 + src/redux/reducers/chatReducer.js | 8 + src/routes/AppRoutes.jsx | 2 +- 10 files changed, 441 insertions(+), 160 deletions(-) diff --git a/src/components/AuditorModal.jsx b/src/components/AuditorModal.jsx index dc1025a4..70836433 100644 --- a/src/components/AuditorModal.jsx +++ b/src/components/AuditorModal.jsx @@ -58,6 +58,7 @@ export default function AuditorModal({ const [mode, setMode] = useState('info'); const [message, setMessage] = useState(''); const [scope, setScope] = useState([]); + const { organizations, own } = useSelector(s => s.organization); const handleInvite = () => { if (user.current_role === CUSTOMER && isAuth() && myProjects.length) { @@ -93,7 +94,7 @@ export default function AuditorModal({ const handleSendMessage = () => { // TODO add check for pm or org chat - if (true) { + if (organizations.length || own.length) { setIsOpenType(true); } else { window.scrollTo(0, 0); @@ -298,16 +299,17 @@ export default function AuditorModal({ Invite to project {!budge && ( - + // + )} diff --git a/src/components/Chat/CurrentChat.jsx b/src/components/Chat/CurrentChat.jsx index 2fc61871..38f44c2b 100644 --- a/src/components/Chat/CurrentChat.jsx +++ b/src/components/Chat/CurrentChat.jsx @@ -19,6 +19,7 @@ import { } from '../../redux/actions/chatActions.js'; import AttachFileModal from './AttachFileModal.jsx'; import Headings from '../../router/Headings.jsx'; +import { useSearchParams } from 'react-router-dom/dist'; const CurrentChat = ({ chatMessages, @@ -30,7 +31,8 @@ const CurrentChat = ({ const navigate = useNavigate(); const { id } = useParams(); const { user } = useSelector(s => s.user); - + const [searchParams] = useSearchParams(); + const orgId = searchParams.get('org'); const [newMessage, setNewMessage] = useState(''); const [attachModalIsOpen, setAttachModalIsOpen] = useState(false); const [displayedMessages, setDisplayedMessages] = useState(20); @@ -43,6 +45,9 @@ const CurrentChat = ({ const messageBoxRef = useRef(); const newMessagesTextRef = useRef(); const userLinkDataRef = useRef({}); + const organization = useSelector(s => + s.organization.organizations.find(el => el.id === orgId), + ); useEffect(() => { if ( diff --git a/src/components/Chat/TypeChat.jsx b/src/components/Chat/TypeChat.jsx index b294c374..3c07516a 100644 --- a/src/components/Chat/TypeChat.jsx +++ b/src/components/Chat/TypeChat.jsx @@ -1,57 +1,241 @@ -import React, { useState } from 'react'; -import { Box, Button, Typography } from '@mui/material'; +import React, { useMemo, useState } from 'react'; +import { + Avatar, + Box, + Button, + Divider, + List, + ListItem, + ListItemAvatar, + ListItemText, + Popover, + Typography, +} from '@mui/material'; +import { addTestsLabel } from '../../lib/helper.js'; +import ChatIcon from '../icons/ChatIcon.jsx'; +import { AUDITOR, CUSTOMER } from '../../redux/actions/types.js'; +import { setCurrentChat } from '../../redux/actions/chatActions.js'; +import { useDispatch, useSelector } from 'react-redux'; +import { useNavigate } from 'react-router-dom'; +import { ASSET_URL } from '../../services/urls.js'; -const TypeChat = () => { +const TypeChat = ({ auditor, project }) => { const [isOpen, setIsOpen] = useState(false); + const [anchorEl, setAnchorEl] = useState(null); + const { chatList } = useSelector(s => s.chat); + const { user } = useSelector(s => s.user); + const { organizations } = useSelector(s => s.organization); + const myAuditor = useSelector(s => s.auditor.auditor); + const myCustomer = useSelector(s => s.customer.customer); + const navigate = useNavigate(); + const dispatch = useDispatch(); + // console.log(myAuditor, myCustomer); + const handleClose = () => { + setAnchorEl(null); + }; + const open = Boolean(anchorEl); + const id = open ? 'simple-popover' : undefined; + + const startChat = () => { + window.scrollTo(0, 0); + + const existingChat = chatList.find(chat => + chat.members?.find( + member => + member.id === auditor?.user_id && + member.role?.toLowerCase() === AUDITOR, + ), + ); + const chatId = existingChat ? existingChat.id : auditor?.user_id; + const members = [auditor?.user_id, user.id]; + + dispatch( + setCurrentChat(chatId, { + name: auditor.first_name, + avatar: auditor.avatar, + role: AUDITOR, + isNew: !existingChat, + members, + }), + ); + localStorage.setItem('path', window.location.pathname); + navigate(`/chat/${existingChat ? existingChat.id : auditor?.user_id}`); + }; + + const myUser = useMemo(() => { + if (user?.current_role?.toLowerCase() === AUDITOR.toLowerCase()) { + return myAuditor; + } else { + return myCustomer; + } + }, [myAuditor, myCustomer]); + + // + const handleSendMessage = e => { + // TODO add check for pm or org chat + if (organizations.length) { + setAnchorEl(e.currentTarget); + } else { + startChat(); + } + }; + + const chatFromOrg = org => { + console.log(org); + window.scrollTo(0, 0); + + if (auditor) { + const existingChat = chatList.find(chat => + chat.members?.find( + member => + member.id === auditor?.user_id && + member.role?.toLowerCase() === AUDITOR, + ), + ); + + const chatId = existingChat ? existingChat.id : auditor?.user_id; + const members = [auditor?.user_id, user.id]; + + dispatch( + setCurrentChat(chatId, { + name: auditor.first_name, + avatar: auditor.avatar, + role: AUDITOR, + isNew: !existingChat, + members, + }), + ); + localStorage.setItem('path', window.location.pathname); + navigate( + `/chat/${existingChat ? existingChat.id : auditor?.user_id}?org=${ + org.id + }`, + ); + } else if (project) { + const existingChat = chatList.find(chat => + chat.members?.find( + member => + member.id === project?.customer_id && + member.role?.toLowerCase() === CUSTOMER, + ), + ); + const chatId = existingChat ? existingChat.id : project?.customer_id; + const members = [project?.customer_id, user.id]; + + dispatch( + setCurrentChat(chatId, { + role: CUSTOMER, + isNew: !existingChat, + userDataId: project?.customer_id, + members, + }), + ); + localStorage.setItem('path', window.location.pathname); + navigate(`/chat/${project?.customer_id}?org=${org.id}`); + } + }; + + // return ( - - + <> + + {/**/} + {/* setIsOpen(!isOpen)}*/} + {/* sx={{ textTransform: 'initial', position: 'relative' }}*/} + {/*>*/} + {/* Organization chat*/} + + - - - - - )} - - + + + + + + + {organizations.map(org => { + console.log(`${ASSET_URL}/${org.avatar}`); + return ( + <> + chatFromOrg(org)} + sx={{ + cursor: 'pointer', + alignItems: 'center', + '&:hover': { + backgroundColor: '#e8e8e8', + }, + }} + > + + + + + // {org.name} + // + // } + /> + + + + ); + })} + + {/**/} + + ); }; diff --git a/src/components/audit-request-info.jsx b/src/components/audit-request-info.jsx index 1cdfaf40..929ca33f 100644 --- a/src/components/audit-request-info.jsx +++ b/src/components/audit-request-info.jsx @@ -50,6 +50,7 @@ import Star from './icons/Star.jsx'; import Currency from './icons/Currency.jsx'; import { ASSET_URL } from '../services/urls.js'; import ListItemButton from '@mui/material/ListItemButton'; +import TypeChat from './Chat/TypeChat.jsx'; const AuditRequestInfo = ({ project, @@ -570,36 +571,39 @@ const AuditRequestInfo = ({ - {organizations.map(org => ( - member.user_id === user.id) - .access_level.some(level => level === 'Editor') - } - onClick={() => { - if ( - org.members - .find(member => member.user_id === user.id) - .access_level.some(level => level === 'Editor') - ) { - handleChose(org); - } - }} - > - - - - - - - - ))} + {organizations.map(org => { + const member = org.members.find( + member => member.user_id === user.id, + ); + const hasEditorAccess = + Array.isArray(member?.access_level) && + member.access_level.some(level => level === 'Editor'); + + return ( + { + if (hasEditorAccess) { + handleChose(org); + } + }} + > + + + + + + + + ); + })} {showAcceptButton && @@ -615,15 +619,16 @@ const AuditRequestInfo = ({ Accept )} - + {/**/} + {/* */} + {/**/} + { const dispatch = useDispatch(); @@ -22,12 +35,13 @@ const ChatPage = () => { const { id } = useParams(); const matchXs = useMediaQuery(theme.breakpoints.down('xs')); const [chatListIsOpen, setChatListIsOpen] = useState(matchXs && !id); - const { chatList, chatMessages, currentChat, error } = useSelector( - s => s.chat, - ); + const { chatList, chatMessages, currentChat, error, orgChatList } = + useSelector(s => s.chat); const { user } = useSelector(s => s.user); const { auditor } = useSelector(s => s.auditor); const { customer } = useSelector(s => s.customer); + const { organizations } = useSelector(s => s.organization); + const [searchParams, setSearchParams] = useSearchParams(); useEffect(() => { if ( @@ -46,6 +60,14 @@ const ChatPage = () => { } }, [user, auditor, customer]); + const profile = useMemo(() => { + if (user.current_role === AUDITOR) { + return auditor; + } else { + return customer; + } + }, [user.current_role]); + useEffect(() => { if (id && !currentChat?.isNew) { const chat = chatList.find(chat => chat.id === id); @@ -74,6 +96,29 @@ const ChatPage = () => { } }; + const handleChoose = org => { + const newSearchParams = new URLSearchParams(searchParams); + + if (org) { + newSearchParams.set('org', org.id); + navigate('/chat' + `?org=${org.id}`); + // + } else { + newSearchParams.delete('org'); + } + + if (newSearchParams.get('org') === 'undefined') { + newSearchParams.delete('org'); + setSearchParams(newSearchParams); + } + }; + + useEffect(() => { + if (searchParams.get('org')) { + dispatch(getChatListByOrg(user.current_role, searchParams.get('org'))); + } + }, [searchParams.get('org')]); + return ( @@ -99,64 +144,67 @@ const ChatPage = () => { sx={{ display: 'flex', flexDirection: 'column', - gap: '20px', + // gap: '10px', borderRight: '2px solid #e5e5e5', - padding: '10px 0', + padding: '13px 0 5px', }} > - - - PM - - - - - Org1 - - - - - Org1 - - - - - Org1 - - + + <> + + + + + + + {organizations.map(org => ( + + handleChoose(org)} + > + + + + ))} @@ -200,6 +248,12 @@ const layoutSx = theme => ({ }, }); +const selectedTab = (theme, primary) => ({ + backgroundColor: primary + ? theme.palette.primary.main + : theme.palette.primary.main, +}); + const wrapper = theme => ({ minHeight: '300px', padding: '20px 40px 100px', diff --git a/src/pages/MyOrganizations.jsx b/src/pages/MyOrganizations.jsx index bde4beb5..29b952f3 100644 --- a/src/pages/MyOrganizations.jsx +++ b/src/pages/MyOrganizations.jsx @@ -79,7 +79,7 @@ const MyOrganization = () => { - {!organizations.length || !own.length ? ( + {!organizations.length && !own.length ? ( diff --git a/src/redux/actions/chatActions.js b/src/redux/actions/chatActions.js index 8f023d33..2a0c27c3 100644 --- a/src/redux/actions/chatActions.js +++ b/src/redux/actions/chatActions.js @@ -15,6 +15,7 @@ import { CHAT_UPDATE_DIFFERENT_ROLE_UNREAD, CHAT_SET_ERROR, CHAT_DELETE_MESSAGE, + CHAT_GET_LIST_ORG, } from './types.js'; export const getChatList = role => { @@ -28,6 +29,20 @@ export const getChatList = role => { }; }; +export const getChatListByOrg = (role, id) => { + const token = Cookies.get('token'); + return dispatch => { + axios + .get(`${API_URL}/chat/preview/${role}?org_id=${id}`, { + headers: { Authorization: `Bearer ${token}` }, + }) + .then(({ data }) => { + console.log(data); + dispatch({ type: CHAT_GET_LIST_ORG, payload: data }); + }); + }; +}; + export const getChatMessages = (chatId, userId) => { const token = Cookies.get('token'); return dispatch => { @@ -68,7 +83,6 @@ export const setCurrentChat = ( const previousChatId = chat?.currentChat?.chatId; if (previousChatId === chatId) return; - if (previousChatId) { const token = Cookies.get('token'); axios.patch( @@ -103,7 +117,14 @@ export const setCurrentChat = ( }; }; -export const chatSendMessage = (text, to, fromRole, isFirst, kind = 'Text') => { +export const chatSendMessage = ( + text, + to, + fromRole, + isFirst, + kind = 'Text', + from_org_id, +) => { const token = Cookies.get('token'); const user = JSON.parse(localStorage.getItem('user')); @@ -122,6 +143,7 @@ export const chatSendMessage = (text, to, fromRole, isFirst, kind = 'Text') => { role: fromRole, text, kind, + from_org_id: from_org_id, }; } else { values = { diff --git a/src/redux/actions/types.js b/src/redux/actions/types.js index e7ea2f71..3c350c86 100644 --- a/src/redux/actions/types.js +++ b/src/redux/actions/types.js @@ -102,6 +102,7 @@ export const NEED_UPDATE = 'NEED_UPDATE'; export const MERGE_ACCOUNT = 'MERGE_ACCOUNT'; export const DELETE_BADGE = 'DELETE_BADGE'; export const CHAT_GET_LIST = 'CHAT_GET_LIST'; +export const CHAT_GET_LIST_ORG = 'CHAT_GET_LIST_ORG'; export const CHAT_GET_MESSAGES = 'CHAT_GET_MESSAGES'; export const CHAT_NEW_MESSAGE = 'CHAT_NEW_MESSAGE'; export const CHAT_DELETE_MESSAGE = 'CHAT_DELETE_MESSAGE'; diff --git a/src/redux/reducers/chatReducer.js b/src/redux/reducers/chatReducer.js index 3788afcb..07dcc6d0 100644 --- a/src/redux/reducers/chatReducer.js +++ b/src/redux/reducers/chatReducer.js @@ -3,6 +3,7 @@ import { CHAT_CLOSE_CURRENT_CHAT, CHAT_DELETE_MESSAGE, CHAT_GET_LIST, + CHAT_GET_LIST_ORG, CHAT_GET_MESSAGES, CHAT_NEW_MESSAGE, CHAT_SEND_FIRST_MESSAGE, @@ -17,6 +18,7 @@ import { const initialState = { chatList: [], chatMessages: [], + orgChatList: [], currentChat: null, unreadMessages: 0, differentRoleUnreadMessages: 0, @@ -39,6 +41,12 @@ export const chatReducer = (state = initialState, action) => { ), }; } + case CHAT_GET_LIST_ORG: { + return { + ...state, + orgChatList: action.payload, + }; + } case AUDIT_REQUEST_CREATE: { return { ...state, diff --git a/src/routes/AppRoutes.jsx b/src/routes/AppRoutes.jsx index 5efd79b7..2abb4c69 100644 --- a/src/routes/AppRoutes.jsx +++ b/src/routes/AppRoutes.jsx @@ -135,7 +135,7 @@ const AppRoutes = () => { dispatch(getChatList(currentRole)); dispatch(getUnreadForDifferentRole()); } - }, [currentRole]); + }, [currentRole, isAuth()]); useEffect(() => { return () => { From 97cff5c3a74c44331e657d8883ebe21e13ead1a5 Mon Sep 17 00:00:00 2001 From: medetkazizov Date: Tue, 15 Oct 2024 15:26:03 +0600 Subject: [PATCH 21/39] chat with organization --- src/components/Chat/ChatList.jsx | 115 +++++++--- src/components/Chat/ChatListItem.jsx | 3 +- src/components/Chat/CurrentChat.jsx | 27 ++- src/components/Chat/Message.jsx | 20 +- src/components/Chat/TypeChat.jsx | 4 +- src/pages/ChatPage.jsx | 242 ++++++++++++++-------- src/pages/Public-profile.jsx | 22 +- src/redux/actions/chatActions.js | 1 + src/redux/actions/organizationAction.js | 23 ++ src/redux/actions/types.js | 1 + src/redux/reducers/organizationReducer.js | 8 + 11 files changed, 330 insertions(+), 136 deletions(-) diff --git a/src/components/Chat/ChatList.jsx b/src/components/Chat/ChatList.jsx index 22e2581b..ee0ce924 100644 --- a/src/components/Chat/ChatList.jsx +++ b/src/components/Chat/ChatList.jsx @@ -14,11 +14,20 @@ import ChatListItem from './ChatListItem.jsx'; import { AUDITOR, CUSTOMER } from '../../redux/actions/types.js'; import { searchAuditor } from '../../redux/actions/auditorAction.js'; import { searchCustomers } from '../../redux/actions/customerAction.js'; +import { searchOrganization } from '../../redux/actions/organizationAction.js'; +import theme from '../../styles/themes.js'; -const ChatList = ({ chatList, chatListIsOpen, setChatListIsOpen }) => { +const ChatList = ({ + chatList, + chatListIsOpen, + setChatListIsOpen, + orgId, + openOrgList, +}) => { const dispatch = useDispatch(); const { auditors } = useSelector(s => s.auditor); const { customers } = useSelector(s => s.customer); + const { searchOrganizations } = useSelector(s => s.organization); const { user } = useSelector(s => s.user); const [, startTransition] = useTransition(); @@ -33,13 +42,16 @@ const ChatList = ({ chatList, chatListIsOpen, setChatListIsOpen }) => { if (search.trim()) { dispatch(searchAuditor({ search, perPage: 20 }, false)); dispatch(searchCustomers({ search, perPage: 20 })); + dispatch(searchOrganization({ search, perPage: 20 })); } }); }, [search]); return ( <> - + { .includes(search.toLowerCase().trim()), ) .reverse() - .map(chat => ( - - )) + .map(chat => { + // const org = chat?.members.find(org => org.org_user_id); + // console.log(chat?.members.find(org => org.org_user_id)); + return ( + + ); + }) : !search && ( You haven't written to anyone yet )} @@ -112,22 +129,25 @@ const ChatList = ({ chatList, chatListIsOpen, setChatListIsOpen }) => { ), ) && auditor.user_id !== user.id, ) - .map(auditor => ( - - ))} + .map(auditor => { + return ( + + ); + })} {search && customers @@ -143,6 +163,7 @@ const ChatList = ({ chatList, chatListIsOpen, setChatListIsOpen }) => { ) .map(customer => ( { /> ))} + {search && + searchOrganizations + .filter( + organization => + !chatList.some(chat => + chat.members.some( + member => + member.id === organization.user_id && + member.role?.toLowerCase() === CUSTOMER, + ), + ) && organization.user_id !== user.id, + ) + .map(organization => { + return ( + + ); + })} + {chatList.length > 0 && !customers.length && !auditors.length && @@ -176,11 +230,14 @@ const ChatList = ({ chatList, chatListIsOpen, setChatListIsOpen }) => { export default ChatList; -const wrapper = theme => ({ - width: '30%', +const wrapper = (theme, openOrgList) => ({ borderRight: '2px solid #e5e5e5', display: 'flex', flexDirection: 'column', + width: `calc(100% - ${openOrgList ? '85px' : '0px'} )`, + [theme.breakpoints.down('md')]: { + width: `calc(100% - ${openOrgList ? '73px' : '0px'})`, + }, [theme.breakpoints.down('xs')]: { display: 'none', }, diff --git a/src/components/Chat/ChatListItem.jsx b/src/components/Chat/ChatListItem.jsx index 19c5b8d0..30566e84 100644 --- a/src/components/Chat/ChatListItem.jsx +++ b/src/components/Chat/ChatListItem.jsx @@ -16,6 +16,7 @@ const ChatListItem = ({ isNew = false, userDataId = false, role, + orgId, }) => { const dispatch = useDispatch(); @@ -54,7 +55,7 @@ const ChatListItem = ({ diff --git a/src/components/Chat/CurrentChat.jsx b/src/components/Chat/CurrentChat.jsx index 38f44c2b..45f2aef6 100644 --- a/src/components/Chat/CurrentChat.jsx +++ b/src/components/Chat/CurrentChat.jsx @@ -15,6 +15,7 @@ import { chatSendMessage, closeCurrentChat, getChatList, + getChatListByOrg, getChatMessages, } from '../../redux/actions/chatActions.js'; import AttachFileModal from './AttachFileModal.jsx'; @@ -59,12 +60,17 @@ const CurrentChat = ({ setChatId(currentChat?.chatId); dispatch(getChatMessages(currentChat.chatId, user.id)); } - }, [currentChat, chatId]); + }, [currentChat, chatId, orgId]); useEffect(() => { if (currentChat?.chatId && id !== currentChat?.chatId) { - navigate(`/chat/${currentChat?.chatId}`); - dispatch(getChatList(user.current_role)); + if (!orgId) { + navigate(`/chat/${currentChat?.chatId}`); + dispatch(getChatList(user.current_role)); + } else { + navigate(`/chat/${currentChat?.chatId}?org=${orgId}`); + dispatch(getChatListByOrg('Organization', orgId)); + } } }, [currentChat?.chatId]); @@ -136,12 +142,22 @@ const CurrentChat = ({ const handleSend = () => { if (!newMessage.trim()) return; + // dispatch( + // chatSendMessage( + // newMessage.trim(), + // { id: currentChat?.chatId, role: currentChat?.role }, + // user.current_role, + // currentChat?.isNew, + // ), + // ); dispatch( chatSendMessage( newMessage.trim(), { id: currentChat?.chatId, role: currentChat?.role }, - user.current_role, + orgId ? 'Organization' : user.current_role, currentChat?.isNew, + 'Text', + orgId && orgId, ), ); setNewMessage(''); @@ -269,6 +285,7 @@ const CurrentChat = ({ {unreadLabel && New messages:} ({ width: '70%', display: 'flex', flexDirection: 'column', - [theme.breakpoints.down('sm')]: { + [theme.breakpoints.down('xs')]: { width: '100%', }, }); diff --git a/src/components/Chat/Message.jsx b/src/components/Chat/Message.jsx index 68064290..2301830d 100644 --- a/src/components/Chat/Message.jsx +++ b/src/components/Chat/Message.jsx @@ -11,7 +11,7 @@ import ImageMessage from './ImageMessage.jsx'; import AuditRequestInfo from '../audit-request-info.jsx'; import AuditMessage from './AuditMessage.jsx'; -const Message = ({ message, user, currentChat, isRead, type }) => { +const Message = ({ message, user, currentChat, isRead, type, orgId }) => { const { customer } = useSelector(state => state.customer); const { auditor } = useSelector(state => state.auditor); @@ -26,8 +26,14 @@ const Message = ({ message, user, currentChat, isRead, type }) => { }, [user.current_role, customer?.avatar, auditor?.avatar]); const getMessageAvatar = () => { - if (message?.from?.id === user?.id) { - return userAvatar ? `${ASSET_URL}/${userAvatar}` : null; + if (orgId) { + if (message?.from?.id === orgId) { + return userAvatar ? `${ASSET_URL}/${userAvatar}` : null; + } + } else { + if (message?.from?.id === user?.id) { + return userAvatar ? `${ASSET_URL}/${userAvatar}` : null; + } } return currentChat?.avatar ? `${ASSET_URL}/${currentChat.avatar}` : null; }; @@ -54,7 +60,13 @@ const Message = ({ message, user, currentChat, isRead, type }) => { }; return ( - + { const myCustomer = useSelector(s => s.customer.customer); const navigate = useNavigate(); const dispatch = useDispatch(); - // console.log(myAuditor, myCustomer); + const handleClose = () => { setAnchorEl(null); }; @@ -191,7 +191,7 @@ const TypeChat = ({ auditor, project }) => { > diff --git a/src/pages/ChatPage.jsx b/src/pages/ChatPage.jsx index a0c31aa5..a939217e 100644 --- a/src/pages/ChatPage.jsx +++ b/src/pages/ChatPage.jsx @@ -5,6 +5,7 @@ import { Avatar, Box, Button, + Collapse, Divider, IconButton, Tooltip, @@ -27,6 +28,7 @@ import { AUDITOR, CUSTOMER } from '../redux/actions/types.js'; import Headings from '../router/Headings.jsx'; import CustomSnackbar from '../components/custom/CustomSnackbar.jsx'; import { ASSET_URL } from '../services/urls.js'; +import KeyboardDoubleArrowLeftIcon from '@mui/icons-material/KeyboardDoubleArrowLeft'; const ChatPage = () => { const dispatch = useDispatch(); @@ -42,6 +44,8 @@ const ChatPage = () => { const { customer } = useSelector(s => s.customer); const { organizations } = useSelector(s => s.organization); const [searchParams, setSearchParams] = useSearchParams(); + const orgId = searchParams.get('org'); + const [open, setOpen] = useState(false); useEffect(() => { if ( @@ -61,28 +65,43 @@ const ChatPage = () => { }, [user, auditor, customer]); const profile = useMemo(() => { - if (user.current_role === AUDITOR) { + if (user.current_role.toLowerCase() === AUDITOR.toLowerCase()) { return auditor; } else { return customer; } - }, [user.current_role]); + }, [user.current_role, auditor, customer]); useEffect(() => { if (id && !currentChat?.isNew) { const chat = chatList.find(chat => chat.id === id); + const chatOrg = orgChatList?.find(chat => chat.id === id); const members = chat?.members.map(member => member.id); + const orgMembers = chatOrg?.members.map(member => member.id); const role = chat?.members.find(member => member.id !== user.id)?.role; - - if (chat) { - dispatch( - setCurrentChat(chat?.id, { - name: chat?.name, - avatar: chat?.avatar, - role, - members, - }), - ); + const orgRole = chatOrg?.members.find( + member => member.id !== user.id, + )?.role; + if (chat || chatOrg) { + if (orgId) { + dispatch( + setCurrentChat(chatOrg?.id, { + name: chatOrg?.name, + avatar: chatOrg?.avatar, + orgRole, + orgMembers, + }), + ); + } else { + dispatch( + setCurrentChat(chat?.id, { + name: chat?.name, + avatar: chat?.avatar, + role, + members, + }), + ); + } } } }, [id, chatList.length]); @@ -115,7 +134,7 @@ const ChatPage = () => { useEffect(() => { if (searchParams.get('org')) { - dispatch(getChatListByOrg(user.current_role, searchParams.get('org'))); + dispatch(getChatListByOrg('Organization', searchParams.get('org'))); } }, [searchParams.get('org')]); @@ -140,74 +159,87 @@ const ChatPage = () => { - - - <> - - - - - - - {organizations.map(org => ( - - handleChoose(org)} - > - - - - ))} + + + + + + <> + + + + {/**/} + + + {organizations.map(org => ( + + handleChoose(org)} + > + + + + ))} + + + - {id ? ( { export default ChatPage; const layoutSx = theme => ({ - paddingY: '40px !important', + padding: '40px !important', + [theme.breakpoints.down('md')]: { + padding: '20px 0 !important', + }, +}); + +const orgListSx = theme => ({ + display: 'flex', + flexDirection: 'column', + width: '85px', + borderRight: '2px solid #e5e5e5', + padding: '0 0 5px', + overflowY: 'auto', + overflowX: 'hidden', + height: '100%', + '::-webkit-scrollbar': { + width: '0px', + }, + [theme.breakpoints.down('md')]: { + width: '73px', + }, +}); + +const leftSideSx = theme => ({ + width: '30%', + display: 'flex', + justifyContent: 'end', [theme.breakpoints.down('xs')]: { - paddingY: '20px !important', + width: 'unset', + }, +}); + +const orgAvatarSx = theme => ({ + width: '73px', + height: '73px', + [theme.breakpoints.down('md')]: { + width: '60px', + height: '60px', }, + // backgroundColor: '#fff', }); const selectedTab = (theme, primary) => ({ backgroundColor: primary - ? theme.palette.primary.main + ? theme.palette.secondary.main : theme.palette.primary.main, }); @@ -264,12 +332,13 @@ const wrapper = theme => ({ alignItems: 'flex-start', gap: '15px', [theme.breakpoints.down('sm')]: { - padding: '30px 30px 50px', + // padding: '30px 30px 50px', minHeight: '300px', }, [theme.breakpoints.down('xs')]: { - padding: '20px 15px 50px', + padding: '20px 40px 50px', minHeight: '300px', + borderRadius: 'unset', }, }); @@ -285,6 +354,9 @@ const chatWrapper = { const selectLabelWrapper = { position: 'relative', width: '70%', + // [theme.breakpoints.down(1760)]: { + // width: '65%', + // }, [theme.breakpoints.down('sm')]: { width: '100%', }, diff --git a/src/pages/Public-profile.jsx b/src/pages/Public-profile.jsx index d8900d97..0d009f17 100644 --- a/src/pages/Public-profile.jsx +++ b/src/pages/Public-profile.jsx @@ -48,6 +48,7 @@ import UserFeedbacks from '../components/UserFeedbacks.jsx'; import WalletConnectIcon from '../components/icons/WalletConnectIcon.jsx'; import { getPublicAuditsAuditor } from '../redux/actions/auditAction.js'; import ProjectCardList from '../components/Project-card-list.jsx'; +import TypeChat from '../components/Chat/TypeChat.jsx'; const PublicProfile = ({ notFoundRedirect = true }) => { const navigate = useNavigate(); @@ -550,16 +551,17 @@ const PublicProfile = ({ notFoundRedirect = true }) => { {data.kind !== 'badge' && isAuth() && data?.user_id !== user?.id && ( - + // + )} {role === AUDITOR && ( diff --git a/src/redux/actions/chatActions.js b/src/redux/actions/chatActions.js index 2a0c27c3..076cb589 100644 --- a/src/redux/actions/chatActions.js +++ b/src/redux/actions/chatActions.js @@ -151,6 +151,7 @@ export const chatSendMessage = ( role: fromRole, text, kind, + from_org_id: from_org_id, }; } diff --git a/src/redux/actions/organizationAction.js b/src/redux/actions/organizationAction.js index a37ecdea..21cc3e0c 100644 --- a/src/redux/actions/organizationAction.js +++ b/src/redux/actions/organizationAction.js @@ -6,13 +6,17 @@ import { CLEAR_ORGANIZATION, CREATE_ORGANIZATION, DELETE_INVITES, + GET_AUDITORS, GET_MY_ORGANIZATION, GET_ORGANIZATION_AUDIT_REQUESTS, GET_ORGANIZATION_BY_ID, + GET_ORGANIZATIONS, NOT_FOUND_ORGANIZATION, UPDATE_ORGANIZATION, } from './types.js'; import { history } from '../../services/history.js'; +import createSearchValues from '../../lib/createSearchValues.js'; +import { isAuth } from '../../lib/helper.js'; const API_URL = import.meta.env.VITE_API_BASE_URL; @@ -82,6 +86,25 @@ export const getMyOrganizations = () => { }; }; +export const searchOrganization = (values, badges = true) => { + const queryString = createSearchValues(values, 'Organization'); + + return dispatch => { + const token = Cookies.get('token'); + axios + .get( + `${API_URL}/search?${queryString}`, + isAuth() ? { headers: { Authorization: `Bearer ${token}` } } : {}, + ) + .then(({ data }) => { + dispatch({ type: GET_ORGANIZATIONS, payload: data }); + }) + .catch(({ response }) => { + console.error(response, 'res'); + }); + }; +}; + export const getOrganizationById = id => { return dispatch => { const token = Cookies.get('token'); diff --git a/src/redux/actions/types.js b/src/redux/actions/types.js index 3c350c86..9b84636f 100644 --- a/src/redux/actions/types.js +++ b/src/redux/actions/types.js @@ -15,6 +15,7 @@ export const AUDITOR_SET_ERROR = 'AUDITOR_SET_ERROR'; export const CUSTOMER_SET_ERROR = 'CUSTOMER_SET_ERROR'; export const GET_CUSTOMER = 'GET_CUSTOMER'; export const GET_CUSTOMERS = 'GET_CUSTOMERS'; +export const GET_ORGANIZATIONS = 'GET_ORGANIZATIONS'; export const GET_AUDITOR = 'GET_AUDITOR'; export const GET_AUDITORS = 'GET_AUDITORS'; export const AUDITOR = 'auditor'; diff --git a/src/redux/reducers/organizationReducer.js b/src/redux/reducers/organizationReducer.js index de7ffeac..3ad7e103 100644 --- a/src/redux/reducers/organizationReducer.js +++ b/src/redux/reducers/organizationReducer.js @@ -9,11 +9,13 @@ import { NOT_FOUND_ORGANIZATION, ADD_MEMBER_IN_ORGANIZATION, CLEAR_MESSAGES, + GET_ORGANIZATIONS, } from '../actions/types.js'; const initialState = { organizations: [], own: [], + searchOrganizations: [], includeMe: [], organization: {}, invites: [], @@ -36,6 +38,12 @@ export const organizationReducer = (state = initialState, action) => { ...state, successMessage: 'User successfully invited', }; + case GET_ORGANIZATIONS: { + return { + ...state, + searchOrganizations: action.payload.result, + }; + } case CLEAR_MESSAGES: return { ...state, From a34bc33f395798ceb319aca23cfbc7fd03cee8b0 Mon Sep 17 00:00:00 2001 From: medetkazizov Date: Fri, 18 Oct 2024 16:13:19 +0600 Subject: [PATCH 22/39] chat redesign of org list --- src/components/Chat/ChatList.jsx | 17 +-- src/components/Chat/TypeChat.jsx | 11 -- src/pages/ChatPage.jsx | 228 ++++++++++++++++--------------- 3 files changed, 125 insertions(+), 131 deletions(-) diff --git a/src/components/Chat/ChatList.jsx b/src/components/Chat/ChatList.jsx index ee0ce924..f854a281 100644 --- a/src/components/Chat/ChatList.jsx +++ b/src/components/Chat/ChatList.jsx @@ -101,7 +101,7 @@ const ChatList = ({ .reverse() .map(chat => { // const org = chat?.members.find(org => org.org_user_id); - // console.log(chat?.members.find(org => org.org_user_id)); + // return ( ({ borderRight: '2px solid #e5e5e5', display: 'flex', flexDirection: 'column', - width: `calc(100% - ${openOrgList ? '85px' : '0px'} )`, - [theme.breakpoints.down('md')]: { - width: `calc(100% - ${openOrgList ? '73px' : '0px'})`, - }, - [theme.breakpoints.down('xs')]: { - display: 'none', - }, + // width: `calc(100% - ${!openOrgList ? '70px' : '0px'} )`, + // width: `30%`, + // [theme.breakpoints.down('md')]: { + // width: `calc(100% - ${!openOrgList ? '73px' : '0px'})`, + // }, + // [theme.breakpoints.down('xs')]: { + // display: 'none', + // }, }); const mobileChatListOpen = theme => ({ diff --git a/src/components/Chat/TypeChat.jsx b/src/components/Chat/TypeChat.jsx index b0d519b4..cba2e198 100644 --- a/src/components/Chat/TypeChat.jsx +++ b/src/components/Chat/TypeChat.jsx @@ -158,16 +158,6 @@ const TypeChat = ({ auditor, project }) => { horizontal: 'left', }} > - {/**/} - {/* setIsOpen(!isOpen)}*/} - {/* sx={{ textTransform: 'initial', position: 'relative' }}*/} - {/*>*/} - {/* Organization chat*/} { ); })} - {/**/} ); diff --git a/src/pages/ChatPage.jsx b/src/pages/ChatPage.jsx index a939217e..7f16c1b3 100644 --- a/src/pages/ChatPage.jsx +++ b/src/pages/ChatPage.jsx @@ -158,113 +158,116 @@ const ChatPage = () => { > - - - - - - - <> - - - - {/**/} - - - {organizations.map(org => ( - - handleChoose(org)} - > - - - - ))} - - - + + + + <> + + + + {/**/} + + + {organizations.map(org => ( + + handleChoose(org)} + > + + + + ))} - {id ? ( - - ) : ( - - setChatListIsOpen(prev => !prev)} - color="inherit" - sx={menuButtonSx} - > - - + + + {/* setOpen(!open)}*/} + {/*>*/} + {/* */} + {/**/} + {/**/} + {/**/} + + + {id ? ( + + ) : ( + + setChatListIsOpen(prev => !prev)} + color="inherit" + sx={menuButtonSx} + > + + - - Please select a chat to start messaging... + + Please select a chat to start messaging... + - - )} + )} + @@ -283,8 +286,9 @@ const layoutSx = theme => ({ const orgListSx = theme => ({ display: 'flex', flexDirection: 'column', - width: '85px', - borderRight: '2px solid #e5e5e5', + // width: '70px', + border: '2px solid #e5e5e5', + borderRight: 'unset', padding: '0 0 5px', overflowY: 'auto', overflowX: 'hidden', @@ -292,9 +296,9 @@ const orgListSx = theme => ({ '::-webkit-scrollbar': { width: '0px', }, - [theme.breakpoints.down('md')]: { - width: '73px', - }, + // [theme.breakpoints.down('md')]: { + // width: '73px', + // }, }); const leftSideSx = theme => ({ @@ -307,8 +311,8 @@ const leftSideSx = theme => ({ }); const orgAvatarSx = theme => ({ - width: '73px', - height: '73px', + width: '60px', + height: '60px', [theme.breakpoints.down('md')]: { width: '60px', height: '60px', @@ -324,7 +328,7 @@ const selectedTab = (theme, primary) => ({ const wrapper = theme => ({ minHeight: '300px', - padding: '20px 40px 100px', + padding: '10px 20px 60px 5px', position: 'relative', display: 'flex', maxWidth: 'unset', From 824985ccd6ae580763a82c1d27011052badeeb00 Mon Sep 17 00:00:00 2001 From: medetkazizov Date: Tue, 22 Oct 2024 16:27:12 +0600 Subject: [PATCH 23/39] changed org list view in chat --- src/components/Chat/ChatList.jsx | 21 ++--- src/components/Chat/ChatListItem.jsx | 5 +- src/components/Chat/Message.jsx | 12 ++- src/pages/ChatPage.jsx | 104 +++++++++++++++------- src/redux/reducers/organizationReducer.js | 1 + 5 files changed, 94 insertions(+), 49 deletions(-) diff --git a/src/components/Chat/ChatList.jsx b/src/components/Chat/ChatList.jsx index f854a281..22aa2cb7 100644 --- a/src/components/Chat/ChatList.jsx +++ b/src/components/Chat/ChatList.jsx @@ -17,13 +17,7 @@ import { searchCustomers } from '../../redux/actions/customerAction.js'; import { searchOrganization } from '../../redux/actions/organizationAction.js'; import theme from '../../styles/themes.js'; -const ChatList = ({ - chatList, - chatListIsOpen, - setChatListIsOpen, - orgId, - openOrgList, -}) => { +const ChatList = ({ chatList, chatListIsOpen, setChatListIsOpen, orgId }) => { const dispatch = useDispatch(); const { auditors } = useSelector(s => s.auditor); const { customers } = useSelector(s => s.customer); @@ -49,9 +43,7 @@ const ChatList = ({ return ( <> - + No search results} - setChatListIsOpen(false)} - /> + {/* setChatListIsOpen(false)}*/} + {/*/>*/} ); }; @@ -234,6 +226,7 @@ const wrapper = (theme, openOrgList) => ({ borderRight: '2px solid #e5e5e5', display: 'flex', flexDirection: 'column', + width: '100%', // width: `calc(100% - ${!openOrgList ? '70px' : '0px'} )`, // width: `30%`, // [theme.breakpoints.down('md')]: { diff --git a/src/components/Chat/ChatListItem.jsx b/src/components/Chat/ChatListItem.jsx index 30566e84..cc679d96 100644 --- a/src/components/Chat/ChatListItem.jsx +++ b/src/components/Chat/ChatListItem.jsx @@ -1,5 +1,5 @@ import React from 'react'; -import { Link as RouterLink } from 'react-router-dom'; +import { Link as RouterLink, useParams } from 'react-router-dom'; import { useDispatch } from 'react-redux'; import { Avatar, Box, Link, Tooltip } from '@mui/material'; import { ASSET_URL } from '../../services/urls.js'; @@ -19,6 +19,7 @@ const ChatListItem = ({ orgId, }) => { const dispatch = useDispatch(); + const { id } = useParams(); const getUnreadForUser = chat => chat.unread.find(unread => unread.id === user.id)?.unread || 0; @@ -53,7 +54,7 @@ const ChatListItem = ({ return ( { {message.kind === 'Image' ? ( diff --git a/src/pages/ChatPage.jsx b/src/pages/ChatPage.jsx index 7f16c1b3..0a827f74 100644 --- a/src/pages/ChatPage.jsx +++ b/src/pages/ChatPage.jsx @@ -45,7 +45,7 @@ const ChatPage = () => { const { organizations } = useSelector(s => s.organization); const [searchParams, setSearchParams] = useSearchParams(); const orgId = searchParams.get('org'); - const [open, setOpen] = useState(false); + const xsMatch = useMediaQuery(theme.breakpoints.down('xs')); useEffect(() => { if ( @@ -121,7 +121,9 @@ const ChatPage = () => { if (org) { newSearchParams.set('org', org.id); navigate('/chat' + `?org=${org.id}`); - // + if (xsMatch) { + setChatListIsOpen(true); + } } else { newSearchParams.delete('org'); } @@ -172,7 +174,7 @@ const ChatPage = () => { <> { { ))} - - {/* setOpen(!open)}*/} - {/*>*/} - {/* */} - {/**/} - {/**/} - {/**/} + + setChatListIsOpen(false)} + /> {id ? ( { export default ChatPage; +const orgListItemSx = theme => ({ + padding: '5px', + borderRadius: '8px', + width: '70px', + [theme.breakpoints.down('sm')]: { + width: '50px', + }, +}); + const layoutSx = theme => ({ padding: '40px !important', [theme.breakpoints.down('md')]: { @@ -283,6 +281,23 @@ const layoutSx = theme => ({ }, }); +const mobileChatListOpenBackground = theme => ({ + display: 'none', + [theme.breakpoints.down('xs')]: { + display: 'block', + width: '30%', + position: 'absolute', + top: 0, + right: 0, + bottom: '-1px', + zIndex: 20, + background: 'rgba(0, 0, 0, .1)', + }, + [theme.breakpoints.down(500)]: { + display: 'none', + }, +}); + const orgListSx = theme => ({ display: 'flex', flexDirection: 'column', @@ -306,16 +321,36 @@ const leftSideSx = theme => ({ display: 'flex', justifyContent: 'end', [theme.breakpoints.down('xs')]: { - width: 'unset', + display: 'none', + }, +}); + +const mobileChatListOpen = theme => ({ + overflowY: 'auto', + '::-webkit-scrollbar': { + width: '4px', + }, + [theme.breakpoints.down('xs')]: { + background: '#fcfaf6', + position: 'absolute', + top: 0, + left: 0, + bottom: 0, + zIndex: 20, + display: 'block', + width: '70%', + }, + [theme.breakpoints.down(500)]: { + width: '100%', }, }); const orgAvatarSx = theme => ({ width: '60px', height: '60px', - [theme.breakpoints.down('md')]: { - width: '60px', - height: '60px', + [theme.breakpoints.down('sm')]: { + width: '40px', + height: '40px', }, // backgroundColor: '#fff', }); @@ -335,25 +370,32 @@ const wrapper = theme => ({ flexDirection: 'column', alignItems: 'flex-start', gap: '15px', + [theme.breakpoints.down('md')]: { + borderRadius: 'unset', + }, [theme.breakpoints.down('sm')]: { // padding: '30px 30px 50px', minHeight: '300px', }, [theme.breakpoints.down('xs')]: { - padding: '20px 40px 50px', + // padding: '20px 40px 50px', minHeight: '300px', - borderRadius: 'unset', + gap: '8px', + padding: '10px 10px 30px', }, }); -const chatWrapper = { +const chatWrapper = theme => ({ height: '70vh', minHeight: '590px', - width: '100%', + width: 'calc(100% - 70px)', display: 'flex', border: '2px solid #e5e5e5', position: 'relative', -}; + [theme.breakpoints.down('sm')]: { + width: 'calc(100% - 50px)', + }, +}); const selectLabelWrapper = { position: 'relative', diff --git a/src/redux/reducers/organizationReducer.js b/src/redux/reducers/organizationReducer.js index d7b53393..a93215f2 100644 --- a/src/redux/reducers/organizationReducer.js +++ b/src/redux/reducers/organizationReducer.js @@ -89,6 +89,7 @@ export const organizationReducer = (state = initialState, action) => { ...state, invites: state.invites.filter(el => el.id !== action.payload.id), organizations: [...state.organizations, action.payload], + organization: action.payload, }; case CLEAR_ORGANIZATION: return { From f293210b8d6ce2a37418acfa2d9412dd6bfd0ecf Mon Sep 17 00:00:00 2001 From: medetkazizov Date: Wed, 30 Oct 2024 15:12:33 +0600 Subject: [PATCH 24/39] fixes of role view in chat for organization --- src/components/Chat/ChatListItem.jsx | 9 +++++++-- src/components/Chat/Message.jsx | 21 ++++++++++----------- 2 files changed, 17 insertions(+), 13 deletions(-) diff --git a/src/components/Chat/ChatListItem.jsx b/src/components/Chat/ChatListItem.jsx index cc679d96..afc9acc2 100644 --- a/src/components/Chat/ChatListItem.jsx +++ b/src/components/Chat/ChatListItem.jsx @@ -24,8 +24,13 @@ const ChatListItem = ({ const getUnreadForUser = chat => chat.unread.find(unread => unread.id === user.id)?.unread || 0; - const getRole = () => - role || chat.members.find(member => member.id !== user.id)?.role; + const getRole = () => { + return ( + role || + chat.members.find(member => (member.org_user_id ?? member.id) !== user.id) + ?.role + ); + }; const setChatHandle = () => { setListIsOpen(false); diff --git a/src/components/Chat/Message.jsx b/src/components/Chat/Message.jsx index 3210b5dd..7bd42c91 100644 --- a/src/components/Chat/Message.jsx +++ b/src/components/Chat/Message.jsx @@ -59,6 +59,12 @@ const Message = ({ message, user, currentChat, isRead, type, orgId }) => { }); }; + const isOwn = () => { + return message.from.role.toLowerCase() === 'organization' + ? { isOwn: message.from.org_user_id === user.id } + : { isOwn: message.from?.id === user.id }; + }; + return ( { + {/*{{}}*/} {message.kind === 'Image' ? ( ) : message.kind === 'Audit' ? ( @@ -157,7 +156,7 @@ const messageAvatarSx = theme => ({ const messageTextSx = ({ isOwn }) => ({ position: 'relative', - minWidth: '150px', + minWidth: '200px', maxWidth: '400px', margin: '0 20px', background: '#e5e5e5', From 28420b3aca2a6800271be373d65c36ab2a78e606 Mon Sep 17 00:00:00 2001 From: medetkazizov Date: Mon, 4 Nov 2024 14:48:31 +0600 Subject: [PATCH 25/39] added page for customer search, fixes of invite user in organization, update of inivites by socket --- src/components/AuditorSearchModal.jsx | 66 +++-- src/components/CustomerListCard.jsx | 240 ++++++++++++++++++ src/components/Organization.jsx | 6 +- .../OrganizationList/OrganizationList.jsx | 16 +- src/components/User-info.jsx | 16 +- src/pages/AuditorsPage.jsx | 3 +- src/pages/CustomersPage.jsx | 234 +++++++++++++++++ src/redux/actions/customerAction.js | 20 ++ src/redux/actions/types.js | 2 + src/redux/middleware/websocketMiddleware.js | 6 + src/redux/reducers/auditorReducer.js | 7 + src/redux/reducers/customerReducer.js | 7 + src/redux/reducers/organizationReducer.js | 12 + src/routes/AppRoutes.jsx | 2 + 14 files changed, 608 insertions(+), 29 deletions(-) create mode 100644 src/components/CustomerListCard.jsx create mode 100644 src/pages/CustomersPage.jsx diff --git a/src/components/AuditorSearchModal.jsx b/src/components/AuditorSearchModal.jsx index ecd57722..34ab023b 100644 --- a/src/components/AuditorSearchModal.jsx +++ b/src/components/AuditorSearchModal.jsx @@ -27,18 +27,18 @@ import dayjs from 'dayjs'; import { DatePicker } from '@mui/x-date-pickers/DatePicker'; import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs'; import { LocalizationProvider } from '@mui/x-date-pickers'; -import { useNavigate } from 'react-router-dom/dist'; +import { useNavigate, useSearchParams } from 'react-router-dom/dist'; import { Field, Formik, Form } from 'formik'; import SalarySlider from './forms/salary-slider/salary-slider.jsx'; import * as Yup from 'yup'; -import { useParams } from 'react-router-dom'; +import { useLocation, useParams } from 'react-router-dom'; import { addTestsLabel } from '../lib/helper.js'; import CustomSnackbar from './custom/CustomSnackbar.jsx'; import PriceCalculation from './PriceCalculation.jsx'; import { ASSET_URL } from '../services/urls.js'; import TotalPrice from './forms/TotalPrice/TotalPrice.jsx'; import { addUserInOrganization } from '../redux/actions/organizationAction.js'; -import { AUDITOR } from '../redux/actions/types.js'; +import { AUDITOR, CLEAR_SEARCH } from '../redux/actions/types.js'; import { searchCustomers } from '../redux/actions/customerAction.js'; import Radio from '@mui/material/Radio'; @@ -49,6 +49,8 @@ export default function AuditorSearchModal({ setState, setError, invite, + modeType, + customer, }) { const navigate = useNavigate(); const dispatch = useDispatch(); @@ -60,25 +62,31 @@ export default function AuditorSearchModal({ const [selectedAuditor, setSelectedAuditor] = useState({}); const organization = useSelector(s => s.organization.organization); const [rulesOfMember, setRulesOfMember] = useState(false); - + const location = useLocation(); const [openDrop, setOpenDrop] = useState(false); - const [mode, setMode] = useState('search'); - + const [mode, setMode] = useState(modeType || 'search'); const [inputValue, setInputValue] = useState(''); const [query, setQuery] = useState(''); useEffect(() => { - if (organization.id) { - if ( - organization.organization_type.toLowerCase() === AUDITOR.toLowerCase() - ) { - dispatch(getAuditors(query, 15)); + if (!modeType) { + if (organization.id) { + if ( + organization.organization_type.toLowerCase() === AUDITOR.toLowerCase() + ) { + dispatch(getAuditors(query, 15)); + } else { + dispatch(searchCustomers({ search: query, perPage: 15 })); + } } else { - dispatch(searchCustomers({ search: query, perPage: 15 })); + dispatch(getAuditors(query, 15)); } - } else { - dispatch(getAuditors(query, 15)); } + return () => { + if (!modeType) { + dispatch({ type: CLEAR_SEARCH }); + } + }; }, [query, organization.id]); const handleInputChange = event => { @@ -97,14 +105,14 @@ export default function AuditorSearchModal({ const handleInviteUser = () => { const data = [ { - user_id: selectedAuditor.user_id, - access_level: rulesOfMember ? 'Editor' : '', + user_id: customer.user_id ? customer.user_id : selectedAuditor.user_id, + access_level: rulesOfMember ? 'Editor' : 'Representative', }, ]; dispatch( addUserInOrganization(organization.link_id, data, organization.id), ); - setMode('search'); + setMode(modeType || 'search'); setQuery(''); handleClose(); }; @@ -116,7 +124,29 @@ export default function AuditorSearchModal({ if (handleSubmit) { handleSubmit(); } - await navigate(`/auditors?search=${query}&projectIdToInvite=${id}`); + if (organization.id) { + if ( + organization.organization_type.toLowerCase() === AUDITOR.toLowerCase() + ) { + await navigate( + `/auditors?search=${query}&organization=${organization.link_id}`, + { + state: { from: location.pathname }, + }, + ); + } else { + await navigate( + `/customers?search=${query}&organization=${organization.link_id}`, + { + state: { from: location.pathname }, + }, + ); + } + } else { + await navigate(`/auditors?search=${query}&projectIdToInvite=${id}`, { + state: { from: location.pathname }, + }); + } }; return ( diff --git a/src/components/CustomerListCard.jsx b/src/components/CustomerListCard.jsx new file mode 100644 index 00000000..0b84ff4a --- /dev/null +++ b/src/components/CustomerListCard.jsx @@ -0,0 +1,240 @@ +import React, { useState } from 'react'; +import { Avatar, Box, Button, Tooltip, Typography } from '@mui/material'; +import TagsList from './tagsList.jsx'; +import CircleIcon from '@mui/icons-material/Circle'; +import theme from '../styles/themes.js'; +import { addTestsLabel, isAuth } from '../lib/helper.js'; +import { useNavigate } from 'react-router-dom'; +import { ASSET_URL } from '../services/urls.js'; +import { useDispatch, useSelector } from 'react-redux'; +import CustomSnackbar from './custom/CustomSnackbar.jsx'; +import AuditorSearchModal from './AuditorSearchModal.jsx'; + +const CustomerListCard = ({ customer, projectIdToInvite, budge }) => { + const navigate = useNavigate(); + const user = useSelector(state => state.user.user); + const [openModal, setOpenModal] = useState(false); + const customerReducer = useSelector(state => state.customer.customer); + const [message, setMessage] = useState(''); + const [isForm, setIsForm] = useState(false); + const myProjects = useSelector(state => state.project.myProjects); + const dispatch = useDispatch(); + const userProjects = useSelector(s => s.project.myProjects); + const [errorMessage, setErrorMessage] = useState(null); + const [showAddUser, setShowAddUser] = useState(false); + + const handleView = () => { + setOpenModal(true); + }; + + const handleCloseModal = () => { + setOpenModal(false); + }; + + const handleError = () => { + setErrorMessage(null); + setMessage('Switched to customer role'); + const delayedFunc = setTimeout(() => { + if (userProjects.length) { + navigate(`/my-projects/${auditor.user_id}`); + } else { + setMessage(null); + setErrorMessage('No active projects'); + } + }, 1000); + return () => clearTimeout(delayedFunc); + }; + + const handleInvite = () => { + setShowAddUser(!showAddUser); + }; + + return ( + + { + setErrorMessage(null); + setMessage(null); + }} + severity={isForm || message ? 'success' : 'error'} + text={message || errorMessage} + /> + { + setShowAddUser(false); + }} + customer={customer} + modeType={'invite'} + setError={() => console.log('error')} + /> + + + + + + + + + + + {customer.first_name} {customer.last_name} + + + + + + + + + + + + + {/**/} + {/* View more*/} + {/**/} + + {budge && not registered} + + + ); +}; + +export default CustomerListCard; + +const budgeTitle = theme => ({ + color: '#B2B3B3', + fontSize: '12px!important', + marginTop: '-10px', + [theme.breakpoints.down('sm')]: { + fontSize: '10px!important', + marginTop: '-5px', + }, +}); + +const wrapper = theme => ({ + padding: '12px 20px 12px 45px', + display: 'flex', + gap: '10px', + height: '100%', + justifyContent: 'space-between', + [theme.breakpoints.down('lg')]: { + padding: '20px', + }, + [theme.breakpoints.down('xs')]: { + padding: '15px', + }, +}); + +const cardLeftSide = { + display: 'flex', + flexDirection: 'column', + justifyContent: 'space-between', + gap: '12px', + [theme.breakpoints.down('xs')]: { + gap: '15px', + }, +}; + +const cardRightSide = { + display: 'flex', + flexDirection: 'column', + alignItems: 'center', + gap: '15px', + [theme.breakpoints.down('xs')]: { + gap: '12px', + }, +}; + +const avatarDescription = theme => ({ + display: 'flex', + flexDirection: 'row', + gap: '30px', + [theme.breakpoints.down('lg')]: { + gap: '20px', + }, + [theme.breakpoints.down('xs')]: { + gap: '10px', + }, +}); + +const descriptionStyle = theme => ({ + display: 'flex', + flexDirection: 'column', + gap: '15px', + [theme.breakpoints.down('xs')]: { + gap: '8px', + }, +}); + +const avatarStyle = theme => ({ + width: '65px', + height: '65px', + [theme.breakpoints.down('xs')]: { + width: '38px', + height: '38px', + }, +}); + +const nameStyle = { + fontWeight: '600', + fontSize: { + zero: '11px', + sm: '14px', + md: '16px', + lg: '18px', + }, + color: '#152BEA', +}; + +const inviteButtonStyle = theme => ({ + width: '130px', + textTransform: 'unset', + boxShadow: '0', + fontWeight: 600, + [theme.breakpoints.down('md')]: { + width: '130px', + }, + [theme.breakpoints.down('sm')]: { + width: '86px', + fontSize: '8px', + }, +}); + +const tagsWrapper = theme => ({ + [theme.breakpoints.down('xs')]: { + maxWidth: '130px', + }, +}); diff --git a/src/components/Organization.jsx b/src/components/Organization.jsx index e0766d27..55d84fd1 100644 --- a/src/components/Organization.jsx +++ b/src/components/Organization.jsx @@ -50,6 +50,7 @@ import AuditorSearchModal from './AuditorSearchModal.jsx'; import ConfirmModal from './modal/ConfirmModal.jsx'; import EditIcon from '@mui/icons-material/Edit'; import UserLIstItem from './UserListItem/UserLIstItem.jsx'; +import { useLocation } from 'react-router-dom'; const Organization = ({ linkId }) => { const role = useSelector(s => s.user.user.current_role); @@ -61,6 +62,7 @@ const Organization = ({ linkId }) => { const invites = useSelector(s => s.organization.invites); const [showAddUser, setShowAddUser] = useState(false); const [openConfirm, setIsOpenConfirm] = useState(false); + const location = useLocation(); const { customer, @@ -126,7 +128,9 @@ const Organization = ({ linkId }) => { position: 'absolute', minWidth: 'unset', }} - onClick={() => navigate('/my-organizations')} + onClick={() => + navigate(location.state?.from || '/', { replace: true }) + } > { const user = useSelector(s => s.user.user); + const location = useLocation(); + return ( {organizations?.map(org => { if (org.avatar) { return ( - + { ); } else { return ( - + { const dispatch = useDispatch(); const navigate = useNavigate(); - const matchXs = useMediaQuery(theme.breakpoints.down('xs')); const matchXxs = useMediaQuery(theme.breakpoints.down(850)); const organizations = useSelector(s => s.organization.organizations); @@ -161,7 +161,7 @@ const UserInfo = ({ role, linkId }) => { )} - {!!organizations.length && ( + {(!!organizations.length || !!invites.length) && ( { )} - - Organization - - + {!!organizations.length && ( + <> + + Organization + + + + )} )} diff --git a/src/pages/AuditorsPage.jsx b/src/pages/AuditorsPage.jsx index 89262ce4..ff6ed17d 100644 --- a/src/pages/AuditorsPage.jsx +++ b/src/pages/AuditorsPage.jsx @@ -82,6 +82,7 @@ const AuditorsPage = () => { return { ...data, page }; }); }; + const previousPath = location.state?.from || '/'; useEffect(() => { if (query) { @@ -104,7 +105,7 @@ const AuditorsPage = () => { + + + + + 0} + count={getNumberOfPages()} + sx={{ mb: '20px' }} + page={currentPage} + onChange={handleChangePage} + showFirstLast={!matchXs} + size="small" + /> + {customers?.length > 0 && ( + + {customers?.map((customer, idx) => ( + + + + ))} + {!matchSm && customers?.length % 2 === 1 && ( + + )} + + )} + {customers?.length === 0 && No results} + 0} + count={getNumberOfPages()} + sx={{ display: 'flex', justifyContent: 'flex-end' }} + page={currentPage} + onChange={handleChangePage} + showFirstLast={!matchXs} + size="small" + /> + + + ); +}; + +export default CustomersPage; + +const wrapper = theme => ({ + width: '100%', + padding: '20px', + backgroundColor: '#FCFAF6', + border: '1.42857px solid #D9D9D9', + boxShadow: + '0px 71.4286px 57.1429px rgba(0, 0, 0, 0.07),' + + ' 0px 29.8412px 23.8729px rgba(0, 0, 0, 0.0503198), ' + + '0px 15.9545px 12.7636px rgba(0, 0, 0, 0.0417275), ' + + '0px 8.94397px 7.15517px rgba(0, 0, 0, 0.035), ' + + '0px 4.75007px 3.80006px rgba(0, 0, 0, 0.0282725), ' + + '0px 1.97661px 1.58129px rgba(0, 0, 0, 0.0196802)', + borderRadius: '10.7143px', + minHeight: '1000px', +}); + +const headWrapper = theme => ({ + display: 'flex', + justifyContent: 'space-between', + mb: '20px', + [theme.breakpoints.down('sm')]: { + mb: '10px', + }, +}); + +const contentWrapper = { + display: 'flex', + flexWrap: 'wrap', + mb: '20px', + borderLeft: '1px solid #B2B3B3', +}; + +const auditorContainerStyle = idx => ({ + maxHeight: '200px', + minHeight: '150px', + borderRight: '1px solid #B2B3B3', + borderBottom: '1px solid #B2B3B3', + borderTop: idx <= 1 ? '1px solid #B2B3B3' : 'none', + width: { + zero: '100%', + sm: '50%', + md: '50%', + lg: '50%', + }, + [theme.breakpoints.down('sm')]: { + borderTop: idx === 0 ? '1px solid #B2B3B3' : 'none', + }, + [theme.breakpoints.down('xs')]: { + height: '130px', + }, +}); + +const fakeContainerStyle = { + width: { + zero: '100%', + sm: '50%', + md: '50%', + lg: '50%', + }, + border: '0.5px solid #B2B3B3', +}; + +const noResults = { + paddingTop: '70px', + display: 'flex', + justifyContent: 'center', + alignItems: 'center', +}; diff --git a/src/redux/actions/customerAction.js b/src/redux/actions/customerAction.js index 24b30f4e..3f8e636f 100644 --- a/src/redux/actions/customerAction.js +++ b/src/redux/actions/customerAction.js @@ -106,6 +106,26 @@ export const createCustomer = values => { }; }; +export const searchCustomer = (values, badges = true) => { + const kind = 'customer'; + const queryString = createSearchValues(values, kind); + + return dispatch => { + const token = Cookies.get('token'); + axios + .get( + `${API_URL}/search?${queryString}`, + isAuth() ? { headers: { Authorization: `Bearer ${token}` } } : {}, + ) + .then(({ data }) => { + dispatch({ type: GET_CUSTOMERS, payload: data }); + }) + .catch(({ response }) => { + console.error(response, 'res'); + }); + }; +}; + export const updateCustomer = (values, redirect = true) => { const token = Cookies.get('token'); return dispatch => { diff --git a/src/redux/actions/types.js b/src/redux/actions/types.js index 9e3b09d4..b91ef89b 100644 --- a/src/redux/actions/types.js +++ b/src/redux/actions/types.js @@ -69,8 +69,10 @@ export const SEND_CONTACT_MESSAGE = 'SEND_CONTACT_MESSAGE'; export const ERROR_CONTACT_MESSAGE = 'ERROR_CONTACT_MESSAGE'; export const RESTORE_PASSWORD = 'RESTORE_PASSWORD'; export const SEND_EMAIL = 'SEND_EMAIL'; +export const ORGANIZATION_INVITE = 'ORGANIZATION_INVITE'; export const WEBSOCKET_CONNECT = 'WEBSOCKET_CONNECT'; export const WEBSOCKET_DISCONNECT = 'WEBSOCKET_DISCONNECT'; +export const CLEAR_SEARCH = 'CLEAR_SEARCH'; export const WEBSOCKET_SEND = 'WEBSOCKET_SEND'; export const WEBSOCKET_CONNECTED = 'WEBSOCKET_CONNECTED'; export const RECEIVE_AUDITOR_MESSAGE = 'RECEIVE_AUDITOR_MESSAGE'; diff --git a/src/redux/middleware/websocketMiddleware.js b/src/redux/middleware/websocketMiddleware.js index 5e3f07a2..36ed2951 100644 --- a/src/redux/middleware/websocketMiddleware.js +++ b/src/redux/middleware/websocketMiddleware.js @@ -6,6 +6,7 @@ import { GET_NEW_REQUEST, IN_PROGRESS, NEED_UPDATE, + ORGANIZATION_INVITE, REQUEST_DECLINE, UPDATE_AUDIT_ISSUE_WS, WEBSOCKET_CONNECT, @@ -108,6 +109,11 @@ const websocketMiddleware = () => { type: UPDATE_AUDIT_ISSUE_WS, payload: message.payload.IssueUpdate, }); + } else if (message.kind.toLowerCase() === 'organizationinvite') { + store.dispatch({ + type: ORGANIZATION_INVITE, + payload: message.payload.OrganizationInvite, + }); } }; diff --git a/src/redux/reducers/auditorReducer.js b/src/redux/reducers/auditorReducer.js index 2f5832e7..12c5bd6e 100644 --- a/src/redux/reducers/auditorReducer.js +++ b/src/redux/reducers/auditorReducer.js @@ -11,6 +11,7 @@ import { SELECT_ROLE, GET_AUDITOR_RATING_DETAILS, CLEAR_CURRENT_AUDITOR_CUSTOMER, + CLEAR_SEARCH, } from '../actions/types.js'; const initialState = { @@ -52,6 +53,12 @@ export const auditorReducer = (state = initialState, action) => { ...state, currentAuditor: action.payload, }; + case CLEAR_SEARCH: + return { + ...state, + searchAuditors: null, + searchTotalAuditors: 0, + }; case SELECT_ROLE: return { ...state, currentAuditor: null }; case LOG_OUT: diff --git a/src/redux/reducers/customerReducer.js b/src/redux/reducers/customerReducer.js index 6446a2b8..f4c15e36 100644 --- a/src/redux/reducers/customerReducer.js +++ b/src/redux/reducers/customerReducer.js @@ -1,6 +1,7 @@ import { CLEAR_CURRENT_AUDITOR_CUSTOMER, CLEAR_MESSAGES, + CLEAR_SEARCH, CUSTOMER_SET_ERROR, GET_CURRENT_CUSTOMER, GET_CUSTOMER, @@ -49,6 +50,12 @@ export const customerReducer = (state = initialState, action) => { ...state, error: action.payload, }; + case CLEAR_SEARCH: + return { + ...state, + searchCustomers: null, + searchTotalCustomers: 0, + }; case CLEAR_MESSAGES: return { ...state, diff --git a/src/redux/reducers/organizationReducer.js b/src/redux/reducers/organizationReducer.js index a93215f2..aa38a490 100644 --- a/src/redux/reducers/organizationReducer.js +++ b/src/redux/reducers/organizationReducer.js @@ -10,6 +10,8 @@ import { ADD_MEMBER_IN_ORGANIZATION, CLEAR_MESSAGES, GET_ORGANIZATIONS, + ORGANIZATION_INVITE, + CLEAR_SEARCH, } from '../actions/types.js'; const initialState = { @@ -79,11 +81,21 @@ export const organizationReducer = (state = initialState, action) => { ...state, organization: action.payload, }; + case ORGANIZATION_INVITE: + return { + ...state, + invites: [...state.invites, action.payload], + }; case DELETE_INVITES: return { ...state, invites: state.invites.filter(el => el.id !== action.payload.id), }; + case CLEAR_SEARCH: + return { + ...state, + searchOrganizations: [], + }; case ACCEPT_INVITE: return { ...state, diff --git a/src/routes/AppRoutes.jsx b/src/routes/AppRoutes.jsx index 99a08809..8cbbac25 100644 --- a/src/routes/AppRoutes.jsx +++ b/src/routes/AppRoutes.jsx @@ -63,6 +63,7 @@ import { getMyOrganizations, } from '../redux/actions/organizationAction.js'; import MyOrganization from '../pages/MyOrganizations.jsx'; +import CustomersPage from '../pages/CustomersPage.jsx'; const AppRoutes = () => { const currentRole = useSelector(s => s.user.user.current_role); @@ -182,6 +183,7 @@ const AppRoutes = () => { } /> } /> } /> + } /> } /> } /> } /> From b6a21cd9595e13c6e3ee4e1445540f697d64d8aa Mon Sep 17 00:00:00 2001 From: medetkazizov Date: Mon, 4 Nov 2024 18:01:29 +0600 Subject: [PATCH 26/39] added link for show user profile in org message author --- src/components/Chat/ChatListItem.jsx | 5 ++-- src/components/Chat/CurrentChat.jsx | 1 + src/components/Chat/Message.jsx | 40 +++++++++++++++++++++++++--- 3 files changed, 40 insertions(+), 6 deletions(-) diff --git a/src/components/Chat/ChatListItem.jsx b/src/components/Chat/ChatListItem.jsx index afc9acc2..d44fd98a 100644 --- a/src/components/Chat/ChatListItem.jsx +++ b/src/components/Chat/ChatListItem.jsx @@ -27,8 +27,9 @@ const ChatListItem = ({ const getRole = () => { return ( role || - chat.members.find(member => (member.org_user_id ?? member.id) !== user.id) - ?.role + chat.members.find( + member => (member?.org_user?.id ?? member.id) !== user.id, + )?.role ); }; diff --git a/src/components/Chat/CurrentChat.jsx b/src/components/Chat/CurrentChat.jsx index 45f2aef6..5d8abc83 100644 --- a/src/components/Chat/CurrentChat.jsx +++ b/src/components/Chat/CurrentChat.jsx @@ -289,6 +289,7 @@ const CurrentChat = ({ message={msg} currentChat={currentChat} isRead={isInterlocutorRead} + chatRole={currentChat?.role} /> ); diff --git a/src/components/Chat/Message.jsx b/src/components/Chat/Message.jsx index 7bd42c91..0d329885 100644 --- a/src/components/Chat/Message.jsx +++ b/src/components/Chat/Message.jsx @@ -2,7 +2,7 @@ import React, { useMemo, useState } from 'react'; import { useSelector } from 'react-redux'; import Cookies from 'js-cookie'; import axios from 'axios'; -import { Avatar, Box, Button, Modal, Typography } from '@mui/material'; +import { Avatar, Box, Button, Modal, Typography, Link } from '@mui/material'; import DoneAllIcon from '@mui/icons-material/DoneAll'; import { ASSET_URL } from '../../services/urls.js'; import theme from '../../styles/themes.js'; @@ -11,7 +11,15 @@ import ImageMessage from './ImageMessage.jsx'; import AuditRequestInfo from '../audit-request-info.jsx'; import AuditMessage from './AuditMessage.jsx'; -const Message = ({ message, user, currentChat, isRead, type, orgId }) => { +const Message = ({ + message, + user, + currentChat, + isRead, + type, + orgId, + chatRole, +}) => { const { customer } = useSelector(state => state.customer); const { auditor } = useSelector(state => state.auditor); @@ -61,7 +69,7 @@ const Message = ({ message, user, currentChat, isRead, type, orgId }) => { const isOwn = () => { return message.from.role.toLowerCase() === 'organization' - ? { isOwn: message.from.org_user_id === user.id } + ? { isOwn: message.from?.org_user?.id === user.id } : { isOwn: message.from?.id === user.id }; }; @@ -81,7 +89,22 @@ const Message = ({ message, user, currentChat, isRead, type, orgId }) => { : messageTextSx(isOwn()) } > - {/*{{}}*/} + {message.from?.org_user?.id && ( + + + {message.from?.org_user.name} + + + )} {message.kind === 'Image' ? ( ) : message.kind === 'Audit' ? ( @@ -131,6 +154,15 @@ function makeLinksClickable(text) { const messageSx = ({ isOwn }) => ({ display: 'flex', flexDirection: isOwn ? 'row-reverse' : 'row', + '& a': { + textDecoration: 'unset', + }, +}); + +const orgNameSx = (theme, color) => ({ + padding: '5px!important', + paddingLeft: '18px!important', + color: `${color}!important`, }); const contentSx = theme => ({ From 9494ae3e7d5a0867ea7ea9ec9fe9a691883e8bb7 Mon Sep 17 00:00:00 2001 From: medetkazizov Date: Tue, 5 Nov 2024 13:49:23 +0600 Subject: [PATCH 27/39] added link to view profile of sender in chat --- src/components/Chat/Message.jsx | 40 ++++++++++++++++++++------------ src/components/Chat/TypeChat.jsx | 4 ++-- src/pages/audit-info.jsx | 27 ++++----------------- 3 files changed, 32 insertions(+), 39 deletions(-) diff --git a/src/components/Chat/Message.jsx b/src/components/Chat/Message.jsx index 0d329885..1bc9e8ac 100644 --- a/src/components/Chat/Message.jsx +++ b/src/components/Chat/Message.jsx @@ -2,7 +2,7 @@ import React, { useMemo, useState } from 'react'; import { useSelector } from 'react-redux'; import Cookies from 'js-cookie'; import axios from 'axios'; -import { Avatar, Box, Button, Modal, Typography, Link } from '@mui/material'; +import { Avatar, Box, Button, Modal, Typography } from '@mui/material'; import DoneAllIcon from '@mui/icons-material/DoneAll'; import { ASSET_URL } from '../../services/urls.js'; import theme from '../../styles/themes.js'; @@ -10,6 +10,8 @@ import { AUDITOR, CUSTOMER } from '../../redux/actions/types.js'; import ImageMessage from './ImageMessage.jsx'; import AuditRequestInfo from '../audit-request-info.jsx'; import AuditMessage from './AuditMessage.jsx'; +import { Link, useLocation } from 'react-router-dom'; +import { useNavigate } from 'react-router-dom/dist'; const Message = ({ message, @@ -22,7 +24,8 @@ const Message = ({ }) => { const { customer } = useSelector(state => state.customer); const { auditor } = useSelector(state => state.auditor); - + const location = useLocation(); + const navigate = useNavigate(); const userAvatar = useMemo(() => { if (user.current_role === AUDITOR && !!auditor?.avatar) { return auditor.avatar; @@ -73,6 +76,15 @@ const Message = ({ : { isOwn: message.from?.id === user.id }; }; + const handleGoProfile = () => { + localStorage.setItem('prev', location.pathname); + navigate( + `/${message.from?.org_user?.role[0].toLowerCase()}/${ + message.from?.org_user?.id + }`, + ); + }; + return ( {message.from?.org_user?.id && ( - - - {message.from?.org_user.name} - - + {message.from?.org_user.name} + )} {message.kind === 'Image' ? ( @@ -163,6 +172,7 @@ const orgNameSx = (theme, color) => ({ padding: '5px!important', paddingLeft: '18px!important', color: `${color}!important`, + cursor: 'pointer', }); const contentSx = theme => ({ diff --git a/src/components/Chat/TypeChat.jsx b/src/components/Chat/TypeChat.jsx index cba2e198..c9caf63c 100644 --- a/src/components/Chat/TypeChat.jsx +++ b/src/components/Chat/TypeChat.jsx @@ -190,7 +190,7 @@ const TypeChat = ({ auditor, project }) => { {organizations.map(org => { console.log(`${ASSET_URL}/${org.avatar}`); return ( - <> + chatFromOrg(org)} @@ -219,7 +219,7 @@ const TypeChat = ({ auditor, project }) => { /> - + ); })} diff --git a/src/pages/audit-info.jsx b/src/pages/audit-info.jsx index 799d692b..ecefb4b5 100644 --- a/src/pages/audit-info.jsx +++ b/src/pages/audit-info.jsx @@ -263,7 +263,11 @@ const AuditInfo = ({ to={`/a/${audit.auditor_id}`} style={{ display: 'grid', textAlign: 'center' }} > - + {audit?.auditor_first_name} @@ -380,27 +384,6 @@ const AuditInfo = ({ )} )} - - - Price: - - - {!!audit?.time?.from && !isPublic && ( From a0b4b6ac1b256dd2f4b9b5b7a8b285eaf7b9384e Mon Sep 17 00:00:00 2001 From: medetkazizov Date: Tue, 5 Nov 2024 13:52:22 +0600 Subject: [PATCH 28/39] added link to view profile of sender in chat --- src/components/Chat/TypeChat.jsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/Chat/TypeChat.jsx b/src/components/Chat/TypeChat.jsx index c9caf63c..f391a1e1 100644 --- a/src/components/Chat/TypeChat.jsx +++ b/src/components/Chat/TypeChat.jsx @@ -188,7 +188,6 @@ const TypeChat = ({ auditor, project }) => { {organizations.map(org => { - console.log(`${ASSET_URL}/${org.avatar}`); return ( Date: Tue, 5 Nov 2024 16:17:17 +0600 Subject: [PATCH 29/39] added link to view profile of sender in chat --- src/components/AuditorSearchModal.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/AuditorSearchModal.jsx b/src/components/AuditorSearchModal.jsx index 34ab023b..a1c3739b 100644 --- a/src/components/AuditorSearchModal.jsx +++ b/src/components/AuditorSearchModal.jsx @@ -105,7 +105,7 @@ export default function AuditorSearchModal({ const handleInviteUser = () => { const data = [ { - user_id: customer.user_id ? customer.user_id : selectedAuditor.user_id, + user_id: customer?.user_id ? customer.user_id : selectedAuditor.user_id, access_level: rulesOfMember ? 'Editor' : 'Representative', }, ]; From b4205f9d4ade11b53d7d9d1f12e03d5d255d7b11 Mon Sep 17 00:00:00 2001 From: medetkazizov Date: Wed, 25 Dec 2024 18:17:25 +0600 Subject: [PATCH 30/39] conflict --- src/components/Chat/Message.jsx | 36 +++++++++++++++++---------------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/src/components/Chat/Message.jsx b/src/components/Chat/Message.jsx index 96431ef3..f1170b07 100644 --- a/src/components/Chat/Message.jsx +++ b/src/components/Chat/Message.jsx @@ -13,15 +13,15 @@ import { Link, useLocation } from 'react-router-dom'; import { useNavigate } from 'react-router-dom/dist'; const Message = ({ message, user, currentChat, isRead }) => { -// const Message = ({ -// message, -// user, -// currentChat, -// isRead, -// type, -// orgId, -// chatRole, -// }) => { + // const Message = ({ + // message, + // user, + // currentChat, + // isRead, + // type, + // orgId, + // chatRole, + // }) => { const { customer } = useSelector(state => state.customer); const { auditor } = useSelector(state => state.auditor); @@ -48,19 +48,21 @@ const Message = ({ message, user, currentChat, isRead }) => { } }, [user.current_role, customer?.avatar, auditor?.avatar]); - const getMessageAvatar = () => {if (orgId) { - // if (message?.from?.id === orgId) { - // return userAvatar ? `${ASSET_URL}/${userAvatar}` : null; - // } - // } else { - // if (message?.from?.id === user?.id) { - // return userAvatar ? `${ASSET_URL}/${userAvatar}` : null; - // } + const getMessageAvatar = () => { + // if (orgId) { + // if (message?.from?.id === orgId) { + // return userAvatar ? `${ASSET_URL}/${userAvatar}` : null; + // } + // } else { + // if (message?.from?.id === user?.id) { + // return userAvatar ? `${ASSET_URL}/${userAvatar}` : null; + // } if (message?.from?.id === user?.id) { return userAvatar ? `${ASSET_URL}/id/${userAvatar}` : null; } return currentChat?.avatar ? `${ASSET_URL}/id/${currentChat.avatar}` : null; }; + // } const downloadFile = () => { const token = Cookies.get('token'); From fc7f7bb8d684a1d7ef3fe12b1d26da72d8f6e646 Mon Sep 17 00:00:00 2001 From: medetkazizov Date: Mon, 30 Dec 2024 10:18:36 +0600 Subject: [PATCH 31/39] fixes of start chat with Organization.jsx --- src/components/Chat/Message.jsx | 48 +++----- src/components/Chat/TypeChat.jsx | 4 +- src/components/Organization.jsx | 3 +- src/components/audit-request-info.jsx | 118 ++++++++++---------- src/pages/MyOrganizations.jsx | 1 - src/redux/actions/chatActions.js | 1 - src/redux/middleware/websocketMiddleware.js | 2 +- 7 files changed, 80 insertions(+), 97 deletions(-) diff --git a/src/components/Chat/Message.jsx b/src/components/Chat/Message.jsx index f1170b07..dabdb2f9 100644 --- a/src/components/Chat/Message.jsx +++ b/src/components/Chat/Message.jsx @@ -12,16 +12,15 @@ import AuditMessage from './AuditMessage.jsx'; import { Link, useLocation } from 'react-router-dom'; import { useNavigate } from 'react-router-dom/dist'; -const Message = ({ message, user, currentChat, isRead }) => { - // const Message = ({ - // message, - // user, - // currentChat, - // isRead, - // type, - // orgId, - // chatRole, - // }) => { +const Message = ({ + message, + user, + currentChat, + isRead, + type, + orgId, + chatRole, +}) => { const { customer } = useSelector(state => state.customer); const { auditor } = useSelector(state => state.auditor); @@ -48,15 +47,14 @@ const Message = ({ message, user, currentChat, isRead }) => { } }, [user.current_role, customer?.avatar, auditor?.avatar]); + const isOwn = () => { + return message.from.role.toLowerCase() === 'organization' + ? { isOwn: message.from?.org_user?.id === user.id } + : { isOwn: message.from?.id === user.id }; + }; + const getMessageAvatar = () => { - // if (orgId) { - // if (message?.from?.id === orgId) { - // return userAvatar ? `${ASSET_URL}/${userAvatar}` : null; - // } - // } else { - // if (message?.from?.id === user?.id) { - // return userAvatar ? `${ASSET_URL}/${userAvatar}` : null; - // } + const avatar = isOwn() ? userAvatar : currentChat?.avatar; if (message?.from?.id === user?.id) { return userAvatar ? `${ASSET_URL}/id/${userAvatar}` : null; } @@ -82,12 +80,6 @@ const Message = ({ message, user, currentChat, isRead }) => { }); }; - const isOwn = () => { - return message.from.role.toLowerCase() === 'organization' - ? { isOwn: message.from?.org_user?.id === user.id } - : { isOwn: message.from?.id === user.id }; - }; - const handleGoProfile = () => { localStorage.setItem('prev', location.pathname); navigate( @@ -98,13 +90,7 @@ const Message = ({ message, user, currentChat, isRead }) => { }; return ( - + { } }, [myAuditor, myCustomer]); - // const handleSendMessage = e => { // TODO add check for pm or org chat if (organizations.length) { @@ -136,7 +135,6 @@ const TypeChat = ({ auditor, project }) => { } }; - // return ( <> - {/**/} - {/* */} - {/* handleChose(auditor)}>*/} - {/* */} - {/* */} - {/* */} - {/* */} - {/* */} - {/* */} - {/* */} - {/* {organizations.map(org => {*/} - {/* const member = org.members.find(*/} - {/* member => member.user_id === user.id,*/} - {/* );*/} - {/* const hasEditorAccess =*/} - {/* Array.isArray(member?.access_level) &&*/} - {/* member.access_level.some(level => level === 'Editor');*/} - - {/* return (*/} - {/* {*/} - {/* if (hasEditorAccess) {*/} - {/* handleChose(org);*/} - {/* }*/} - {/* }}*/} - {/* >*/} - {/* */} - {/* */} - {/* */} - {/* */} - {/* */} - {/* */} - {/* */} - {/* );*/} - {/* })}*/} - {/* */} - {/**/} + + + handleChose(auditor)}> + + + + + + + + {organizations.map(org => { + const member = org.members.find( + member => member.user_id === user.id, + ); + const hasEditorAccess = + member.access_level === 'Editor' || + member.access_level === 'Owner'; + + return ( + { + if (hasEditorAccess) { + handleChose(org); + } + }} + > + + + + + + + + ); + })} + + {showAcceptButton && auditRequest && !isModal && diff --git a/src/pages/MyOrganizations.jsx b/src/pages/MyOrganizations.jsx index 01d67db1..8b5907b7 100644 --- a/src/pages/MyOrganizations.jsx +++ b/src/pages/MyOrganizations.jsx @@ -294,7 +294,6 @@ const gridItemSx = theme => ({ const wrapper = theme => ({ display: 'flex', flexDirection: 'column', - maxWidth: '1300px', width: '100%', '& ul': { fontSize: '16px', diff --git a/src/redux/actions/chatActions.js b/src/redux/actions/chatActions.js index d206c662..8898ff75 100644 --- a/src/redux/actions/chatActions.js +++ b/src/redux/actions/chatActions.js @@ -38,7 +38,6 @@ export const getChatListByOrg = (role, id) => { headers: { Authorization: `Bearer ${token}` }, }) .then(({ data }) => { - console.log(data); dispatch({ type: CHAT_GET_LIST_ORG, payload: data }); }); }; diff --git a/src/redux/middleware/websocketMiddleware.js b/src/redux/middleware/websocketMiddleware.js index bf586680..b3727f08 100644 --- a/src/redux/middleware/websocketMiddleware.js +++ b/src/redux/middleware/websocketMiddleware.js @@ -93,7 +93,7 @@ const websocketMiddleware = () => { } else if (message.kind.toLowerCase() === 'chatmessage') { const sameRole = store.getState().user.user.current_role.toLowerCase() === - message.user_role.toLowerCase(); + message?.user_role?.toLowerCase(); store.dispatch( receiveNewChatMessage(message.payload.ChatMessage, sameRole), ); From 876f1994da1c8e9037bef294f9b19ae7eff3dea5 Mon Sep 17 00:00:00 2001 From: medetkazizov Date: Thu, 16 Jan 2025 15:23:16 +0600 Subject: [PATCH 32/39] fixes of organization card view and fixes of layout organizations pages --- .../IdentitySetting/IdentitySetting.jsx | 3 + src/components/Organization.jsx | 67 ++-- src/components/OrganizationCard.jsx | 115 +++++++ .../OrganizationList/OrganizationList.jsx | 2 +- src/pages/ChatPage.jsx | 77 ++--- src/pages/MyOrganizations.jsx | 288 ++++++------------ 6 files changed, 283 insertions(+), 269 deletions(-) create mode 100644 src/components/OrganizationCard.jsx diff --git a/src/components/IdentitySetting/IdentitySetting.jsx b/src/components/IdentitySetting/IdentitySetting.jsx index b2ad2062..c8a43bb1 100644 --- a/src/components/IdentitySetting/IdentitySetting.jsx +++ b/src/components/IdentitySetting/IdentitySetting.jsx @@ -250,6 +250,9 @@ const buttonSx = theme => ({ fontSize: '18px', width: '214px', borderRadius: '10px', + [theme.breakpoints.down(850)]: { + width: '234px', + }, [theme.breakpoints.down('xs')]: { padding: '9px 10px', }, diff --git a/src/components/Organization.jsx b/src/components/Organization.jsx index b379e2ba..0c640bb2 100644 --- a/src/components/Organization.jsx +++ b/src/components/Organization.jsx @@ -120,22 +120,22 @@ const Organization = ({ linkId }) => { return ( + - ({ marginTop: '7px', }, }, + position: 'relative', + padding: '25px 30px 60px', + [theme.breakpoints.down('md')]: { + padding: '20px 24px 20px', + }, + [theme.breakpoints.down('sm')]: { + gap: '20px', + padding: '30px 10px 20px', + '& h3': { + fontSize: '20px', + }, + }, + [theme.breakpoints.down(780)]: { + borderRadius: '0!important', + }, }); const aboutWrapper = theme => ({ @@ -439,27 +454,7 @@ const aboutWrapper = theme => ({ }, }); -const innerWrapper = theme => ({ - width: '100%', - minHeight: '520px', - display: 'flex', - flexDirection: 'column', - padding: '60px 40px 40px', - gap: '30px', - justifyContent: 'space-between', - [theme.breakpoints.down('sm')]: { - gap: '20px', - padding: '20px', - }, - [theme.breakpoints.down('xs')]: { - width: '100%', - alignItems: 'center', - gap: '25px', - '& .mobile-tag-wrapper': { - maxWidth: '380px', - }, - }, -}); +const innerWrapper = theme => ({}); const infoInnerStyle = theme => ({ display: 'flex', diff --git a/src/components/OrganizationCard.jsx b/src/components/OrganizationCard.jsx new file mode 100644 index 00000000..4a3bcda6 --- /dev/null +++ b/src/components/OrganizationCard.jsx @@ -0,0 +1,115 @@ +import React from 'react'; +import { CUSTOMER } from '../redux/actions/types.js'; +import theme from '../styles/themes.js'; +import { Avatar, Box, Button, Typography } from '@mui/material'; +import { ASSET_URL } from '../services/urls.js'; +import PeopleIcon from '@mui/icons-material/People.js'; +import { useSelector } from 'react-redux'; +import { useNavigate } from 'react-router-dom/dist'; + +const OrganizationCard = ({ org }) => { + const role = useSelector(s => s.user.user.current_role); + const navigate = useNavigate(); + + return ( + + + + + {org.name} + + + + {org.members.length} + + {org.organization_type} + + + + + ); +}; + +export default OrganizationCard; + +const wrapper = (theme, role) => ({ + backgroundColor: '#fff', + display: 'flex', + flexDirection: 'column', + alignItems: 'center', + padding: '20px', + gap: '15px', + borderRadius: '1.5rem', + border: `1px solid ${ + role === CUSTOMER + ? theme.palette.primary.main + : theme.palette.secondary.main + }!important`, + [theme.breakpoints.down('sm')]: { + padding: '15px 10px', + }, +}); + +const avatarSx = theme => ({ + height: '200px', + width: '200px', + [theme.breakpoints.down('md')]: { + width: '150px', + height: '150px', + }, + [theme.breakpoints.down('xs')]: { + width: '100px', + height: '100px', + }, +}); + +const infoWrapper = theme => ({ + mt: '10px', + display: 'flex', + alignItems: 'center', + justifyContent: 'space-between', + [theme.breakpoints.down('md')]: { + '& p': { + fontSize: '16px', + }, + }, + [theme.breakpoints.down('sm')]: { + '& p': { + fontSize: '14px', + }, + }, +}); + +const buttonSx = theme => ({ + width: '100%', + borderRadius: '8px', + textTransform: 'unset', + [theme.breakpoints.down('md')]: { + fontSize: '16px', + }, + [theme.breakpoints.down('sm')]: { + fontSize: '14px', + }, +}); + +const iconSx = theme => ({ + [theme.breakpoints.down('md')]: { + width: '18px', + height: '18px', + }, +}); diff --git a/src/components/OrganizationList/OrganizationList.jsx b/src/components/OrganizationList/OrganizationList.jsx index e1d9d994..8a34f828 100644 --- a/src/components/OrganizationList/OrganizationList.jsx +++ b/src/components/OrganizationList/OrganizationList.jsx @@ -22,7 +22,7 @@ const OrganizationList = ({ organizations }) => { > { }} > - - <> - + <> + + - - {/**/} - - + + + {organizations.map(org => ( - - handleChoose(org)} - > + handleChoose(org)} + > + - - + + ))} @@ -268,6 +270,7 @@ export default ChatPage; const orgListItemSx = theme => ({ padding: '5px', borderRadius: '8px', + cursor: 'pointer', width: '70px', [theme.breakpoints.down('sm')]: { width: '50px', diff --git a/src/pages/MyOrganizations.jsx b/src/pages/MyOrganizations.jsx index 8b5907b7..0fb83901 100644 --- a/src/pages/MyOrganizations.jsx +++ b/src/pages/MyOrganizations.jsx @@ -22,7 +22,8 @@ import Headings from '../router/Headings.jsx'; import Layout from '../styles/Layout.jsx'; import Loader from '../components/Loader.jsx'; import { CustomCard } from '../components/custom/Card.jsx'; -import InfoCard from '../components/custom/info-card.jsx'; +import PeopleIcon from '@mui/icons-material/People'; +import OrganizationCard from '../components/OrganizationCard.jsx'; const MyOrganization = () => { const role = useSelector(s => s.user.user.current_role); @@ -64,194 +65,74 @@ const MyOrganization = () => { return ( - + + + + {!organizations.length && !own.length ? ( + + + + ) : ( + <> + {!!own.length && ( + + My organizations + + {own?.map(org => { + return ( + + + + ); + })} + + + )} + {!!organizations.length && ( + + Organizations + + {organizations?.map(org => { + return ( + + + + ); + })} + + + )} + + )} + - - - {!organizations.length && !own.length ? ( - - - - ) : ( - <> - {!!own.length && ( - - My organizations - - {own?.map(org => { - return ( - - - - {org.avatar ? ( - - ) : ( - - - {org.name.slice(0, 3)} - - - )} - - {org.name} - - - - - ); - })} - - - )} - {!!organizations.length && ( - - Organizations - - {organizations?.map(org => { - return ( - - - - {org.avatar ? ( - - ) : ( - - - {org.name.slice(0, 3)} - - - )} - - {org.name} - - - - - ); - })} - - - )} - - )} - - - @@ -276,6 +157,14 @@ const buttonSx = theme => ({ }, }); +const gridSx = theme => ({ + mt: '5px!important', + marginLeft: '-16px', + [theme.breakpoints.down('xs')]: { + marginLeft: '-8px', + }, +}); + const gridItemSx = theme => ({ width: '20%', [theme.breakpoints.down('md')]: { @@ -295,6 +184,7 @@ const wrapper = theme => ({ display: 'flex', flexDirection: 'column', width: '100%', + position: 'relative', '& ul': { fontSize: '16px', marginBottom: '28px', @@ -303,6 +193,20 @@ const wrapper = theme => ({ marginTop: '7px', }, }, + padding: '25px 30px 60px', + [theme.breakpoints.down('md')]: { + padding: '20px 24px 20px', + }, + [theme.breakpoints.down('sm')]: { + gap: '20px', + padding: '30px 10px 20px', + '& h3': { + fontSize: '20px', + }, + }, + [theme.breakpoints.down(780)]: { + borderRadius: '0!important', + }, }); const innerWrapper = theme => ({ @@ -310,18 +214,12 @@ const innerWrapper = theme => ({ minHeight: '520px', display: 'flex', flexDirection: 'column', - padding: '40px 40px 40px', gap: '30px', justifyContent: 'space-between', - [theme.breakpoints.down('sm')]: { - gap: '20px', - padding: '20px', - paddingTop: '55px', - - '& h4': { - fontSize: '25px', - }, + '& h4': { + textAlign: 'center', }, + [theme.breakpoints.down('sm')]: {}, [theme.breakpoints.down('xs')]: { width: '100%', gap: '25px', From 41f18cea6983f4696639f3ee557974bafef96e5f Mon Sep 17 00:00:00 2001 From: medetkazizov Date: Thu, 16 Jan 2025 16:36:10 +0600 Subject: [PATCH 33/39] changed button depend of role --- src/components/OrganizationCard.jsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/OrganizationCard.jsx b/src/components/OrganizationCard.jsx index 4a3bcda6..d1fb1467 100644 --- a/src/components/OrganizationCard.jsx +++ b/src/components/OrganizationCard.jsx @@ -38,6 +38,7 @@ const OrganizationCard = ({ org }) => { onClick={() => navigate(`/o/${org.link_id}`)} sx={buttonSx} variant={'contained'} + color={role === CUSTOMER ? 'primary' : 'secondary'} > View more From eea86879281eb503473c20890e0b5784ec121e61 Mon Sep 17 00:00:00 2001 From: medetkazizov Date: Tue, 21 Jan 2025 15:13:17 +0600 Subject: [PATCH 34/39] fixes of create request for project from auditor organization, changed view of modal for role, fixes of alert for create request --- src/components/AuditorModal.jsx | 1 - src/components/AuditorSearchModal.jsx | 75 ++++-- src/components/CreateProjectCard.jsx | 27 +++ src/components/Organization.jsx | 2 +- src/components/OrganizationCard.jsx | 2 +- src/components/UserListItem/UserLIstItem.jsx | 224 +++++++++++------- src/components/audit-request-info.jsx | 2 +- .../PublicProjectCard.jsx | 12 +- src/pages/MyOrganizations.jsx | 58 +++-- 9 files changed, 276 insertions(+), 127 deletions(-) diff --git a/src/components/AuditorModal.jsx b/src/components/AuditorModal.jsx index a60e84a1..9a8de264 100644 --- a/src/components/AuditorModal.jsx +++ b/src/components/AuditorModal.jsx @@ -27,7 +27,6 @@ import { import * as Yup from 'yup'; import CustomSnackbar from './custom/CustomSnackbar.jsx'; import ShareProfileButton from './custom/ShareProfileButton.jsx'; -import PriceCalculation from './PriceCalculation.jsx'; import { setCurrentChat } from '../redux/actions/chatActions.js'; import ChatIcon from './icons/ChatIcon.jsx'; import { getAuditorRating } from '../redux/actions/auditorAction.js'; diff --git a/src/components/AuditorSearchModal.jsx b/src/components/AuditorSearchModal.jsx index 81545860..ba819e91 100644 --- a/src/components/AuditorSearchModal.jsx +++ b/src/components/AuditorSearchModal.jsx @@ -37,7 +37,7 @@ import CustomSnackbar from './custom/CustomSnackbar.jsx'; import PriceCalculation from './PriceCalculation.jsx'; import { ASSET_URL } from '../services/urls.js'; import TotalPrice from './forms/TotalPrice/TotalPrice.jsx'; -import { CLEAR_SEARCHED_AUDITOR } from '../redux/actions/types.js'; +import { CLEAR_SEARCHED_AUDITOR, CUSTOMER } from '../redux/actions/types.js'; import { addUserInOrganization } from '../redux/actions/organizationAction.js'; import { AUDITOR, CLEAR_SEARCH } from '../redux/actions/types.js'; import { searchCustomers } from '../redux/actions/customerAction.js'; @@ -63,8 +63,8 @@ export default function AuditorSearchModal({ const customerReducer = useSelector(state => state.customer); const [selectedAuditor, setSelectedAuditor] = useState({}); const organization = useSelector(s => s.organization.organization); - const [rulesOfMember, setRulesOfMember] = useState(false); - const location = useLocation(); + const [rulesOfMember, setRulesOfMember] = useState('Representative'); + const user = useSelector(s => s.user.user); const [openDrop, setOpenDrop] = useState(false); const [mode, setMode] = useState(modeType || 'search'); const [inputValue, setInputValue] = useState(''); @@ -108,7 +108,7 @@ export default function AuditorSearchModal({ const data = [ { user_id: customer?.user_id ? customer.user_id : selectedAuditor.user_id, - access_level: rulesOfMember ? 'Editor' : 'Representative', + access_level: rulesOfMember, }, ]; dispatch( @@ -408,31 +408,61 @@ export default function AuditorSearchModal({ gap: '20px', }} > - {/*}*/} - {/* label="Representative"*/} - {/* labelPlacement="top"*/} - {/* onChange={e => {*/} - {/* setRulesOfMember({*/} - {/* ...rulesOfMember,*/} - {/* representative: e.target.checked,*/} - {/* });*/} - {/* }}*/} - {/*/>*/} + setRulesOfMember('Owner')} + /> + } + sx={{ width: '30%' }} + label="Owner" + labelPlacement="top" + disabled + /> { - setRulesOfMember(!rulesOfMember); - }} - checked={rulesOfMember} + checked={rulesOfMember === 'Editor'} + color={ + user?.current_role?.toLowerCase() === + CUSTOMER?.toLowerCase() + ? 'primary' + : 'secondary' + } + onChange={() => setRulesOfMember('Editor')} /> } + sx={{ width: '30%' }} label="Editor" labelPlacement="top" /> + setRulesOfMember('Representative')} + /> + } + sx={{ width: '30%' }} + label="Representative" + labelPlacement="top" + /> + setRulesOfMember('Owner')} + /> + } + sx={{ width: '30%' }} + label="Owner" + labelPlacement="top" + /> + setRulesOfMember('Editor')} + /> + } + sx={{ width: '30%' }} + label="Editor" + labelPlacement="top" + /> + setRulesOfMember('Representative')} + /> + } + sx={{ width: '30%' }} + label="Representative" + labelPlacement="top" + /> + - handleChangeOwner()} - handleDisagree={() => setIsOpenConfirm(false)} - isOpen={isOpenConfirm} - /> @@ -188,7 +241,7 @@ const UserLIstItem = ({ value, labelId, organization }) => { @@ -204,14 +257,21 @@ export default UserLIstItem; const modalSx = theme => ({ position: 'absolute', - width: 400, bgcolor: 'background.paper', borderRadius: '8px', boxShadow: 24, - p: 4, + p: 3, top: '50%', left: '50%', transform: 'translate(-50%, -50%)', + backgroundColor: 'white', + padding: '10px', + width: '700px', + [theme.breakpoints.down('sm')]: { + paddingBottom: '30px', + height: '100%', + width: '100%', + }, [theme.breakpoints.down(500)]: { width: '310px', p: 2, diff --git a/src/components/audit-request-info.jsx b/src/components/audit-request-info.jsx index 0a9558b7..fc624c9d 100644 --- a/src/components/audit-request-info.jsx +++ b/src/components/audit-request-info.jsx @@ -611,7 +611,7 @@ const AuditRequestInfo = ({ disableScrollLock > { const navigate = useNavigate(); @@ -24,7 +26,10 @@ const PublicProjectCard = ({ project }) => { const handleView = e => { setOpenModal(true); }; - + const dispatch = useDispatch(); + const { auditRequest, auditRequests, successMessage } = useSelector( + s => s.audits, + ); const handleCloseModal = () => { setOpenModal(false); }; @@ -59,13 +64,14 @@ const PublicProjectCard = ({ project }) => { { + dispatch(clearMessage()); setMessage(null); setErrorMessage(null); }} severity={errorMessage ? 'error' : 'success'} - text={message || errorMessage} + text={message || errorMessage || successMessage} /> diff --git a/src/pages/MyOrganizations.jsx b/src/pages/MyOrganizations.jsx index 0fb83901..dde0247a 100644 --- a/src/pages/MyOrganizations.jsx +++ b/src/pages/MyOrganizations.jsx @@ -17,13 +17,11 @@ import theme from '../styles/themes.js'; import { useNavigate, useParams, Link } from 'react-router-dom/dist'; import { useDispatch, useSelector } from 'react-redux'; import { AUDITOR, CUSTOMER } from '../redux/actions/types.js'; -import { ASSET_URL } from '../services/urls.js'; -import Headings from '../router/Headings.jsx'; import Layout from '../styles/Layout.jsx'; import Loader from '../components/Loader.jsx'; import { CustomCard } from '../components/custom/Card.jsx'; -import PeopleIcon from '@mui/icons-material/People'; import OrganizationCard from '../components/OrganizationCard.jsx'; +import Badge from '@mui/material/Badge'; const MyOrganization = () => { const role = useSelector(s => s.user.user.current_role); @@ -33,6 +31,7 @@ const MyOrganization = () => { const matchXxs = useMediaQuery(theme.breakpoints.down(590)); const own = useSelector(s => s.organization.own); const organizations = useSelector(s => s.organization.includeMe); + const invites = useSelector(s => s.organization.invites); const loading = useSelector(s => s.organization.loading); const { @@ -47,21 +46,6 @@ const MyOrganization = () => { } = useSelector(s => s.auditor); const { user, error } = useSelector(s => s.user); - // if (!organizations.length && !own.length) { - // return ( - // - // - // - // - // - // - // ); - // } else { return ( @@ -84,6 +68,38 @@ const MyOrganization = () => { ) : ( <> + {!!invites.length && ( + + Invites + + {invites?.map(org => { + return ( + + + + + + ); + })} + + + )} {!!own.length && ( My organizations @@ -167,6 +183,12 @@ const gridSx = theme => ({ const gridItemSx = theme => ({ width: '20%', + '& .MuiBadge-root': { + width: '100%', + }, + '& .org-card': { + width: '100%', + }, [theme.breakpoints.down('md')]: { width: '25%', }, From c14b4bb039f97c0385511f08c370170dc98cda0a Mon Sep 17 00:00:00 2001 From: medetkazizov Date: Tue, 21 Jan 2025 18:42:25 +0600 Subject: [PATCH 35/39] added title for rules in organization --- src/components/AuditorSearchModal.jsx | 142 +++++++++------- src/components/UserListItem/UserLIstItem.jsx | 164 ++++++++++--------- 2 files changed, 177 insertions(+), 129 deletions(-) diff --git a/src/components/AuditorSearchModal.jsx b/src/components/AuditorSearchModal.jsx index ba819e91..e09cbf49 100644 --- a/src/components/AuditorSearchModal.jsx +++ b/src/components/AuditorSearchModal.jsx @@ -404,65 +404,83 @@ export default function AuditorSearchModal({ sx={{ mt: '10px', display: 'flex', - justifyContent: 'center', gap: '20px', + flexDirection: 'column', + alignItems: 'flex-start', }} > - setRulesOfMember('Owner')} - /> - } - sx={{ width: '30%' }} - label="Owner" - labelPlacement="top" - disabled - /> - setRulesOfMember('Editor')} - /> - } - sx={{ width: '30%' }} - label="Editor" - labelPlacement="top" - /> - setRulesOfMember('Representative')} - /> - } - sx={{ width: '30%' }} - label="Representative" - labelPlacement="top" - /> + + setRulesOfMember('Owner')} + /> + } + sx={{ marginX: '0' }} + label="Owner" + labelPlacement="right" + disabled + /> + + Has full control over organization management. + + + + setRulesOfMember('Editor')} + /> + } + label="Editor" + sx={{ marginX: '0' }} + labelPlacement="right" + /> + + Can manage audits and communicate on behalf of the + organization. + + + + setRulesOfMember('Representative')} + /> + } + label="Representative" + sx={{ marginX: '0' }} + labelPlacement="right" + /> + + Can communicate on behalf of the organization but cannot + manage audits. + + - + <> ({ const orgListSx = theme => ({ display: 'flex', flexDirection: 'column', - // width: '70px', + minWidth: '72px', border: '2px solid #e5e5e5', borderRight: 'unset', padding: '0 0 5px', @@ -321,9 +313,9 @@ const orgListSx = theme => ({ '::-webkit-scrollbar': { width: '0px', }, - // [theme.breakpoints.down('md')]: { - // width: '73px', - // }, + [theme.breakpoints.down('sm')]: { + minWidth: '52px', + }, }); const leftSideSx = theme => ({ @@ -373,40 +365,42 @@ const selectedTab = (theme, primary) => ({ const wrapper = theme => ({ minHeight: '300px', - padding: '10px 20px 60px 5px', + padding: '0px 20px 20px', position: 'relative', display: 'flex', maxWidth: 'unset', flexDirection: 'column', alignItems: 'flex-start', - gap: '15px', - [theme.breakpoints.down('md')]: { - borderRadius: 'unset', - }, [theme.breakpoints.down('sm')]: { minHeight: '300px', }, [theme.breakpoints.down(780)]: { paddingX: '10px', borderRadius: '0', - // [theme.breakpoints.down('xs')]: { - // // padding: '20px 40px 50px', - // minHeight: '300px', - // gap: '8px', - // padding: '10px 10px 30px', + // [theme.breakpoints.down('xs')]: { + // // padding: '20px 40px 50px', + // minHeight: '300px', + // gap: '8px', + // padding: '10px 10px 30px', + }, +}); + +const chatInnerWrapper = theme => ({ + display: 'flex', + flexDirection: 'row', + width: '100%', + height: 'calc(100vh - 160px)', + [theme.breakpoints.down('sm')]: { + height: 'calc(100vh - 130px)', }, }); const chatWrapper = { - height: 'calc(100vh - 126px)', width: '100%', display: 'flex', border: '2px solid #e5e5e5', position: 'relative', - [theme.breakpoints.down('sm')]: { - width: 'calc(100% - 50px)', - }, -}); +}; const selectLabelWrapper = { position: 'relative', From b38200c1ec8f68444a55d76d5af2f24064a56e1f Mon Sep 17 00:00:00 2001 From: medetkazizov Date: Thu, 13 Feb 2025 15:52:41 +0600 Subject: [PATCH 37/39] fixes of git scope selection --- src/components/CreateProjectCard.jsx | 11 +++++++++-- src/components/GithubSelection/CommitModal.jsx | 2 +- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/components/CreateProjectCard.jsx b/src/components/CreateProjectCard.jsx index ee9c856d..75d31874 100644 --- a/src/components/CreateProjectCard.jsx +++ b/src/components/CreateProjectCard.jsx @@ -152,8 +152,15 @@ const CreateProjectCard = ({ projectInfo }) => { // }, // TODO: replace to git block scope: { - type: projectInfo?.scope?.type || SCOPE_LINKS, - content: projectInfo?.scope?.content || [], + type: projectInfo?.scope?.type || SCOPE_GIT_BLOCK, + content: projectInfo?.scope?.content || { + repository: { + clone_url: null, + display_url: null, + }, + commit: null, + files: [], + }, }, }; diff --git a/src/components/GithubSelection/CommitModal.jsx b/src/components/GithubSelection/CommitModal.jsx index feb07534..04a661d1 100644 --- a/src/components/GithubSelection/CommitModal.jsx +++ b/src/components/GithubSelection/CommitModal.jsx @@ -189,7 +189,7 @@ const CommitModal = ({ }; const checkDiff = useMemo(() => { - if (selected.length !== field.value.content.files.length) { + if (selected.length !== field.value.content?.files?.length) { return false; } return !!selected.every(file => From 21c378a96b2b66f0a3c3e5fc925da2383e8f75be Mon Sep 17 00:00:00 2001 From: medetkazizov Date: Fri, 7 Mar 2025 10:11:13 +0600 Subject: [PATCH 38/39] fixes of infinity loading in my organization page --- src/pages/MyOrganizations.jsx | 13 +++++++++++-- src/redux/actions/organizationAction.js | 2 ++ src/redux/actions/types.js | 1 + src/redux/reducers/organizationReducer.js | 13 +++++++++++++ 4 files changed, 27 insertions(+), 2 deletions(-) diff --git a/src/pages/MyOrganizations.jsx b/src/pages/MyOrganizations.jsx index dde0247a..6317e9e2 100644 --- a/src/pages/MyOrganizations.jsx +++ b/src/pages/MyOrganizations.jsx @@ -22,6 +22,7 @@ import Loader from '../components/Loader.jsx'; import { CustomCard } from '../components/custom/Card.jsx'; import OrganizationCard from '../components/OrganizationCard.jsx'; import Badge from '@mui/material/Badge'; +import { CLEAR_NOT_FOUND_ERROR } from '../redux/actions/types.js'; const MyOrganization = () => { const role = useSelector(s => s.user.user.current_role); @@ -33,7 +34,7 @@ const MyOrganization = () => { const organizations = useSelector(s => s.organization.includeMe); const invites = useSelector(s => s.organization.invites); const loading = useSelector(s => s.organization.loading); - + const errorRequest = useSelector(s => s.organization.errorRequest); const { customer, error: customerError, @@ -46,6 +47,14 @@ const MyOrganization = () => { } = useSelector(s => s.auditor); const { user, error } = useSelector(s => s.user); + useEffect(() => { + return () => { + if (errorRequest) { + dispatch({type: CLEAR_NOT_FOUND_ERROR}); + } + } + }, [errorRequest]); + return ( @@ -62,7 +71,7 @@ const MyOrganization = () => { - {!organizations.length && !own.length ? ( + {!organizations.length && !own.length && !errorRequest ? ( diff --git a/src/redux/actions/organizationAction.js b/src/redux/actions/organizationAction.js index 21cc3e0c..136f14d5 100644 --- a/src/redux/actions/organizationAction.js +++ b/src/redux/actions/organizationAction.js @@ -13,6 +13,7 @@ import { GET_ORGANIZATIONS, NOT_FOUND_ORGANIZATION, UPDATE_ORGANIZATION, + ERROR_GET_MY_ORGANIZATIONS, } from './types.js'; import { history } from '../../services/history.js'; import createSearchValues from '../../lib/createSearchValues.js'; @@ -81,6 +82,7 @@ export const getMyOrganizations = () => { dispatch({ type: GET_MY_ORGANIZATION, payload: data }); }) .catch(({ response }) => { + dispatch({ type: ERROR_GET_MY_ORGANIZATIONS }); console.log(response, 'res'); }); }; diff --git a/src/redux/actions/types.js b/src/redux/actions/types.js index 17d4500c..3dd222d9 100644 --- a/src/redux/actions/types.js +++ b/src/redux/actions/types.js @@ -164,6 +164,7 @@ export const UPDATE_ORGANIZATION = 'UPDATE_ORGANIZATION'; export const CLEAR_ORGANIZATION = 'CLEAR_ORGANIZATION'; export const GET_ORGANIZATION_BY_ID = 'GET_ORGANIZATION_BY_ID'; export const GET_MY_ORGANIZATION = 'GET_MY_ORGANIZATION'; +export const ERROR_GET_MY_ORGANIZATIONS = 'ERROR_GET_MY_ORGANIZATIONS'; export const CLEAR_NOT_FOUND_ERROR = 'CLEAR_NOT_FOUND_ERROR'; export const GET_AUDIT_HISTORY = 'GET_AUDIT_HISTORY'; export const READ_AUDIT_HISTORY = 'READ_AUDIT_HISTORY'; diff --git a/src/redux/reducers/organizationReducer.js b/src/redux/reducers/organizationReducer.js index aa38a490..32439141 100644 --- a/src/redux/reducers/organizationReducer.js +++ b/src/redux/reducers/organizationReducer.js @@ -12,6 +12,8 @@ import { GET_ORGANIZATIONS, ORGANIZATION_INVITE, CLEAR_SEARCH, + ERROR_GET_MY_ORGANIZATIONS, + CLEAR_NOT_FOUND_ERROR } from '../actions/types.js'; const initialState = { @@ -24,6 +26,7 @@ const initialState = { notFound: false, successMessage: '', loading: true, + errorRequest: false, }; export const organizationReducer = (state = initialState, action) => { @@ -37,6 +40,16 @@ export const organizationReducer = (state = initialState, action) => { invites: action.payload.invites, loading: false, }; + case ERROR_GET_MY_ORGANIZATIONS: + return { + ...state, + errorRequest: true, + }; + case CLEAR_NOT_FOUND_ERROR: + return { + ...state, + errorRequest: false, + }; case ADD_MEMBER_IN_ORGANIZATION: return { ...state, From 23a41b66091ebab16045df5a97bd7139b2099f0f Mon Sep 17 00:00:00 2001 From: medetkazizov Date: Wed, 12 Mar 2025 18:36:37 +0600 Subject: [PATCH 39/39] fixes of search organization in invite modal of project, fixes of avatar organization and added logic for request from customer to auditor organization --- src/components/Audit-request.jsx | 4 +- src/components/AuditorSearchModal.jsx | 217 +++++++++++++++--- src/components/Organization.jsx | 25 +- src/components/Project-card.jsx | 41 ++++ src/components/Projects.jsx | 4 +- src/components/audit-request-info.jsx | 18 +- .../custom/AuditorSearchListBox.jsx | 18 +- src/components/forms/Avatar-form/index.jsx | 55 ++--- .../CreateEditOrganizationForm.jsx | 58 ++++- .../edit-profile-form/edit-profile-form.jsx | 1 + src/pages/MyOrganizations.jsx | 6 +- src/pages/audit-info.jsx | 23 +- src/redux/actions/auditAction.js | 30 +++ src/redux/actions/auditorAction.js | 14 +- src/redux/actions/types.js | 2 + src/redux/reducers/auditReducer.js | 13 ++ src/routes/AppRoutes.jsx | 3 +- 17 files changed, 444 insertions(+), 88 deletions(-) diff --git a/src/components/Audit-request.jsx b/src/components/Audit-request.jsx index f279e91d..eb92703c 100644 --- a/src/components/Audit-request.jsx +++ b/src/components/Audit-request.jsx @@ -1,10 +1,12 @@ -import React from 'react'; +import React, { useEffect } from 'react'; import { Box, Button, Grid } from '@mui/material'; import AuditRequestCard from './Audit-request-card'; import { useSelector } from 'react-redux'; import { addTestsLabel } from '../lib/helper.js'; import { useNavigate } from 'react-router-dom/dist'; import Headings from '../router/Headings.jsx'; +import { useDispatch } from 'react-redux'; +import { getOrganizationAuditRequests } from '../redux/actions/auditAction.js'; const AuditRequest = () => { const auditRequests = useSelector(s => s.audits.auditRequests); diff --git a/src/components/AuditorSearchModal.jsx b/src/components/AuditorSearchModal.jsx index 39e1298e..aba4521e 100644 --- a/src/components/AuditorSearchModal.jsx +++ b/src/components/AuditorSearchModal.jsx @@ -82,6 +82,14 @@ export default function AuditorSearchModal({ const [searchValue, setSearchValue] = useState(''); const scrollTimeout = useRef(null); const listInnerRef = useRef(); + const [auditorPagination, setAuditorPagination] = useState({ + hasMore: true, + total: 0, + }); + const [organizationPagination, setOrganizationPagination] = useState({ + hasMore: true, + total: 0, + }); useEffect(() => { dispatch(getAuditors(query, 15)); @@ -175,7 +183,7 @@ export default function AuditorSearchModal({ }; useEffect(() => { - const fetchAuditors = async () => { + const fetchResults = async () => { try { setIsLoading(true); setPage(1); @@ -186,87 +194,184 @@ export default function AuditorSearchModal({ setScrollPosition(listInnerRef.current.scrollTop); } - const response = await axios.get( - `${API_URL}/search?query=${query}&sort_by=rating&tags=&sort_order=-1&page=1&per_page=15&kind=${type} badge`, - { headers: { Authorization: `Bearer ${token}` } }, + const requests = [ + axios.get( + `${API_URL}/search?query=${query}&sort_by=rating&tags=&sort_order=-1&page=1&per_page=15&kind=auditor badge`, + { headers: { Authorization: `Bearer ${token}` } }, + ), + axios.get( + `${API_URL}/search?query=${query}&sort_by=rating&tags=&sort_order=-1&page=1&per_page=15&kind=organization`, + { headers: { Authorization: `Bearer ${token}` } }, + ), + ]; + + const [auditorsResponse, organizationsResponse] = await Promise.all( + requests, ); - setAuditors(response.data.result); + // Update pagination info for both types + setAuditorPagination({ + hasMore: + auditorsResponse.data.result.length > 0 && + auditorsResponse.data.result.length < + auditorsResponse.data.totalDocuments, + total: auditorsResponse.data.totalDocuments, + }); + + setOrganizationPagination({ + hasMore: + organizationsResponse.data.result.length > 0 && + organizationsResponse.data.result.length < + organizationsResponse.data.totalDocuments, + total: organizationsResponse.data.totalDocuments, + }); + + const combinedResults = [ + ...auditorsResponse.data.result, + ...organizationsResponse.data.result, + ]; + + setAuditors(combinedResults); } catch (error) { - console.error('Error fetching auditors:', error); + console.error('Error fetching results:', error); } finally { setIsLoading(false); } }; if (query) { - fetchAuditors(); + fetchResults(); } }, [query]); useEffect(() => { - if (!isLoading && listInnerRef.current && scrollPosition > 0) { - requestAnimationFrame(() => { - listInnerRef.current.scrollTop = scrollPosition; - }); - } - }, [isLoading, auditors]); - - useEffect(() => { - const fetchMoreAuditors = async () => { - if (isLoading || lastList) return; + const fetchMoreResults = async () => { + if ( + isLoading || + (!auditorPagination.hasMore && !organizationPagination.hasMore) + ) + return; try { setIsLoading(true); const token = Cookies.get('token'); - const response = await axios.get( - `${API_URL}/search?query=${query}&sort_by=rating&tags=&sort_order=-1&page=${page}&per_page=15&kind=${type} badge`, - { headers: { Authorization: `Bearer ${token}` } }, - ); - if (response.data.result.length === 0) { + const requests = []; + + // Only fetch auditors if there are more to fetch + if (auditorPagination.hasMore) { + requests.push( + axios.get( + `${API_URL}/search?query=${query}&sort_by=rating&tags=&sort_order=-1&page=${page}&per_page=15&kind=auditor badge`, + { headers: { Authorization: `Bearer ${token}` } }, + ), + ); + } + + // Only fetch organizations if there are more to fetch + if (organizationPagination.hasMore) { + requests.push( + axios.get( + `${API_URL}/search?query=${query}&sort_by=rating&tags=&sort_order=-1&page=${page}&per_page=15&kind=organization`, + { headers: { Authorization: `Bearer ${token}` } }, + ), + ); + } + + if (requests.length === 0) { + setLastList(true); + return; + } + + const responses = await Promise.all(requests); + let newResults = []; + + responses.forEach((response, index) => { + const isAuditorResponse = auditorPagination.hasMore && index === 0; + const isOrgResponse = + organizationPagination.hasMore && + index === (auditorPagination.hasMore ? 1 : 0); + + if (isAuditorResponse) { + setAuditorPagination(prev => ({ + ...prev, + hasMore: + response.data.result.length > 0 && + page * 15 < response.data.totalDocuments, + })); + } + + if (isOrgResponse) { + setOrganizationPagination(prev => ({ + ...prev, + hasMore: + response.data.result.length > 0 && + page * 15 < response.data.totalDocuments, + })); + } + + newResults = [...newResults, ...response.data.result]; + }); + + if (newResults.length === 0) { setLastList(true); return; } setAuditors(prev => { - const newAuditors = response.data.result; - const uniqueAuditors = [...prev]; + const uniqueResults = [...prev]; - newAuditors.forEach(newAuditor => { + newResults.forEach(newItem => { if ( - !uniqueAuditors.some( - existing => existing.user_id === newAuditor.user_id, + !uniqueResults.some( + existing => + existing.user_id === newItem.user_id || + existing.id === newItem.id, ) ) { - uniqueAuditors.push(newAuditor); + uniqueResults.push(newItem); } }); - return uniqueAuditors; + return uniqueResults; }); } catch (error) { - console.error('Error fetching more auditors:', error); + console.error('Error fetching more results:', error); } finally { setIsLoading(false); } }; if (page > 1) { - fetchMoreAuditors(); + fetchMoreResults(); } - }, [page, query, lastList]); + }, [ + page, + query, + lastList, + auditorPagination.hasMore, + organizationPagination.hasMore, + ]); const handleScroll = useCallback( _.throttle(e => { - if (!isLoading && !lastList) { + if ( + !isLoading && + !lastList && + (auditorPagination.hasMore || organizationPagination.hasMore) + ) { const { scrollTop, scrollHeight, clientHeight } = e.target; if (scrollHeight - scrollTop <= clientHeight * 1.2) { setPage(prev => prev + 1); } } }, 300), - [isLoading, lastList, page], + [ + isLoading, + lastList, + auditorPagination.hasMore, + organizationPagination.hasMore, + ], ); const handleSearchInput = e => { @@ -284,6 +389,42 @@ export default function AuditorSearchModal({ }, 300); }; + // const renderSearchResult = item => { + // const isOrganization = type === 'organization'; + // return ( + // handleOptionChange(item)} + // > + // + // + // + // {isOrganization + // ? item.name + // : `${item.first_name} ${item.last_name}`} + // + // {item.description && ( + // + // {item.description} + // + // )} + // + // + // ); + // }; + return ( { setShowAddUser(false); }} setError={() => console.log('error')} - type={organization?.organization_type?.toLowerCase()} + type={ + organization?.organization_type?.toLowerCase() === + AUDITOR.toLowerCase() + ? 'auditor' + : 'organization' + } /> {/* { {organization.members.map(value => { const labelId = `checkbox-list-secondary-label-${value}`; return ( - + + + ); })} @@ -466,6 +474,11 @@ const infoInnerStyle = theme => ({ const userListSx = theme => ({ width: '100%', + [theme.breakpoints.down('sm')]: { + '& li': { + marginLeft: '0px!important', + }, + }, }); const infoStyle = theme => ({ diff --git a/src/components/Project-card.jsx b/src/components/Project-card.jsx index 3a446b3b..d94eaf17 100644 --- a/src/components/Project-card.jsx +++ b/src/components/Project-card.jsx @@ -232,6 +232,22 @@ const ProjectCard = ({ type, project, currentRole, isPublic }) => { label="Publish" /> )} + {!isPublic && project?.auditor_organization?.id && + project?.status.toLowerCase() !== RESOLVED.toLowerCase() && + + Organization + + {project?.auditor_organization?.name} + + + } {type !== AUDITOR && ( - {!organizations.length && !own.length && !errorRequest ? ( + {!organizations.length && !own.length && loading && !errorRequest ? ( @@ -88,7 +88,7 @@ const MyOrganization = () => { > {invites?.map(org => { return ( - + { > {own?.map(org => { return ( - + ); diff --git a/src/pages/audit-info.jsx b/src/pages/audit-info.jsx index 3d376aa1..b2aca5cf 100644 --- a/src/pages/audit-info.jsx +++ b/src/pages/audit-info.jsx @@ -74,7 +74,11 @@ const AuditInfo = ({ const [isFeedbackModalOpen, setIsFeedbackModalOpen] = useState(false); const handleConfirm = () => { - dispatch(confirmAudit(audit, true)); + if (audit?.auditor_organization) { + dispatch(confirmAudit({ ...audit, auditor_organization: audit?.auditor_organization.id }, true)); + } else { + dispatch(confirmAudit(audit, true)); + } }; const handleDecline = () => { @@ -263,6 +267,22 @@ const AuditInfo = ({ isPublic ? { alignItems: 'flex-start' } : {}, ]} > + { + audit?.auditor_organization ? + + : + } {!!audit?.time?.from && !isPublic && ( Time for project: diff --git a/src/redux/actions/auditAction.js b/src/redux/actions/auditAction.js index 6e9892ee..8021c83e 100644 --- a/src/redux/actions/auditAction.js +++ b/src/redux/actions/auditAction.js @@ -21,6 +21,7 @@ import { GET_AUDITS_OF_AUDITOR, GET_PUBLIC_AUDIT, GET_PUBLIC_REPORT, + GET_ORGANIZATION_AUDITS, GET_REQUEST, IN_PROGRESS, NOT_FOUND, @@ -33,6 +34,7 @@ import { SET_CURRENT_AUDIT_PARTNER, UPDATE_AUDIT, VERIFY_AUDIT_REPORT, + GET_ORG_AUDIT_REQUEST, } from './types.js'; import { history } from '../../services/history.js'; import { ASSET_URL } from '../../services/urls.js'; @@ -70,6 +72,23 @@ export const createRequest = (values, redirect, navigateTo, stay) => { }; }; +export const getOrganizationAuditRequests = (org_id) => { + return dispatch => { + const token = Cookies.get('token'); + axios.get(`${API_URL}/audit_request/organization/id/${org_id}`, { + headers: { Authorization: `Bearer ${token}` }, + }) + .then(({ data }) => { + dispatch({ type: GET_ORG_AUDIT_REQUEST, payload: data }); + }) + .catch(({ response }) => { + console.log(response, 'res'); + dispatch({ type: REQUEST_ERROR }); + }); + }; +}; + + export const createRequestModal = values => { return dispatch => { const token = Cookies.get('token'); @@ -147,6 +166,17 @@ export const getAudits = role => { }; }; +export const getOrganizationAudits = () => { + return dispatch => { + const token = Cookies.get('token'); + axios.get(`${API_URL}/audit/organization/all`, { + headers: { Authorization: `Bearer ${token}` }, + }) + .then(({ data }) => { + dispatch({ type: GET_ORGANIZATION_AUDITS, payload: data }); + }); + }; +}; export const getPublicAudit = (id, code) => { return dispatch => { const token = Cookies.get('token'); diff --git a/src/redux/actions/auditorAction.js b/src/redux/actions/auditorAction.js index 755386b6..b65dcbb6 100644 --- a/src/redux/actions/auditorAction.js +++ b/src/redux/actions/auditorAction.js @@ -177,9 +177,9 @@ export const getAuditorRating = (id, getDetails = false) => { axios.get(url).then(({ data }) => { dispatch({ type: GET_AUDITOR_RATING_DETAILS, payload: data }); - }); - }; -}; + }) + } +} export const searchAuditor = (values, badges = true) => { const kind = badges ? 'auditor badge' : 'auditor'; @@ -196,10 +196,10 @@ export const searchAuditor = (values, badges = true) => { dispatch({ type: GET_AUDITORS, payload: data }); }) .catch(({ response }) => { - console.error(response, 'res'); - }); - }; -}; + console.error(response, 'res') + }) + } +} export const deleteBadgeProfile = id => { return dispatch => { diff --git a/src/redux/actions/types.js b/src/redux/actions/types.js index 3dd222d9..f44e6206 100644 --- a/src/redux/actions/types.js +++ b/src/redux/actions/types.js @@ -167,10 +167,12 @@ export const GET_MY_ORGANIZATION = 'GET_MY_ORGANIZATION'; export const ERROR_GET_MY_ORGANIZATIONS = 'ERROR_GET_MY_ORGANIZATIONS'; export const CLEAR_NOT_FOUND_ERROR = 'CLEAR_NOT_FOUND_ERROR'; export const GET_AUDIT_HISTORY = 'GET_AUDIT_HISTORY'; +export const GET_ORG_AUDIT_REQUEST = 'GET_ORG_AUDIT_REQUEST'; export const READ_AUDIT_HISTORY = 'READ_AUDIT_HISTORY'; export const READ_AUDIT_REQUEST_HISTORY = 'READ_AUDIT_REQUEST_HISTORY'; export const ADD_MEMBER_IN_ORGANIZATION = 'ADD_MEMBER_IN_ORGANIZATION'; export const EDIT_AUDIT_CUSTOMER = 'EDIT_AUDIT_CUSTOMER'; +export const GET_ORGANIZATION_AUDITS = 'GET_ORGANIZATION_AUDITS'; export const DELETE_INVITES = 'DELETE_INVITES'; export const ACCEPT_INVITE = 'ACCEPT_INVITE'; export const NOT_FOUND_ORGANIZATION = 'NOT_FOUND_ORGANIZATION'; diff --git a/src/redux/reducers/auditReducer.js b/src/redux/reducers/auditReducer.js index a9f36465..36218c0f 100644 --- a/src/redux/reducers/auditReducer.js +++ b/src/redux/reducers/auditReducer.js @@ -37,6 +37,8 @@ import { UPDATE_AUDIT, CREATE_AUDIT_ISSUE, GET_ORGANIZATION_AUDIT_REQUESTS, + GET_ORG_AUDIT_REQUEST, + GET_ORGANIZATION_AUDITS } from '../actions/types.js'; const initialState = { @@ -54,6 +56,7 @@ const initialState = { unreadHistory: null, auditRequestHistory: [], organizationAuditRequests: [], + organizationAudits: [], verifyAudit: null, }; @@ -66,6 +69,16 @@ export const auditReducer = (state = initialState, action) => { successMessage: 'Audit request created successfully', auditRequest: action.payload, }; + case GET_ORG_AUDIT_REQUEST: + return { + ...state, + organizationAuditRequests: [...state.organizationAuditRequests, ...action.payload], + }; + case GET_ORGANIZATION_AUDITS: + return { + ...state, + organizationAudits: [...state.organizationAudits, ...action.payload], + }; case GET_AUDIT_REQUEST: return { ...state, auditRequests: action.payload }; case DELETE_REQUEST: diff --git a/src/routes/AppRoutes.jsx b/src/routes/AppRoutes.jsx index 5107a829..d328a43b 100644 --- a/src/routes/AppRoutes.jsx +++ b/src/routes/AppRoutes.jsx @@ -14,7 +14,7 @@ import { getCustomer } from '../redux/actions/customerAction.js'; import ProjectPage from '../pages/Project-page.jsx'; import AuditRequestPage from '../pages/Audit-Request-Page.jsx'; import { getProjects } from '../redux/actions/projectAction.js'; -import { getAudits, getAuditsRequest } from '../redux/actions/auditAction.js'; +import { getAudits, getAuditsRequest, getOrganizationAudits } from '../redux/actions/auditAction.js'; import EditProject from '../pages/EditProject.jsx'; import ForCustomer from '../pages/For-customer.jsx'; import ForAuditor from '../pages/For-auditor.jsx'; @@ -101,6 +101,7 @@ const AppRoutes = () => { if (currentRole) { dispatch(getAuditsRequest(currentRole)); dispatch(getAudits(currentRole)); + dispatch(getOrganizationAudits()); } } }, [currentRole, isAuth()]);