From a7d93486d0c6536b9d92e3a46fc3f690120e1d10 Mon Sep 17 00:00:00 2001 From: Andrii17s Date: Fri, 18 Feb 2022 18:20:03 +0200 Subject: [PATCH] [WIP][DO-2925] Turn projects into organizations --- src/components/nav/TopBar.jsx | 46 ++++- .../pages/payment/ProjectDetail.jsx | 171 ++++++++++++++---- src/components/pages/profile/ProfilePage.jsx | 45 ++++- 3 files changed, 217 insertions(+), 45 deletions(-) diff --git a/src/components/nav/TopBar.jsx b/src/components/nav/TopBar.jsx index 46281058..df9656e5 100644 --- a/src/components/nav/TopBar.jsx +++ b/src/components/nav/TopBar.jsx @@ -1,7 +1,7 @@ -import React, { useRef } from 'react'; +import React, { useEffect, useRef, useState } from 'react'; // import PropTypes from 'prop-types'; import { - ChevronRight, User, Settings, HelpCircle, ToggleRight, Clipboard, File, + ChevronRight, User, Settings, HelpCircle, ToggleRight, Clipboard, File, Check, } from 'react-feather'; import { Link, useLocation } from 'react-router-dom'; import { useDispatch, useSelector } from 'react-redux'; @@ -10,6 +10,8 @@ import TopBarSearch from 'components/nav/TopBarSearch'; import getBreadcrumbName from 'const/breadcrumbsNames'; import { useTranslation } from 'react-i18next'; import Notifications from 'components/nav/Notifications'; +import Api from '../../api'; +import Tooltip from '../Tooltip'; // TODO: finish this @@ -18,9 +20,38 @@ const TopBar = () => { const dispatch = useDispatch(); const isShown = useSelector((store) => store.interface.topBarShow); const user = useSelector((store) => store.user); + const [projects, setProjects] = useState([]); + const [verified, setVerified] = useState(false); + const [link, setLink] = useState(''); const { pathname } = useLocation(); + const fetchData = () => { + Api.get('payment/project/') + .then((resp) => { + setProjects(resp.data); + }); + }; + + useEffect(() => { + fetchData(); + }, []); + + const projectLink = (projects) => { + let link = '/system/profile/projects/'; + projects.forEach((project) => { + if (project.is_default) { + link = link.concat(project.id); + if (project.verified) { setVerified(true); } + } + }); + setLink(link); + }; + + useEffect(() => { + projectLink(projects); + }, [projects]); + const breadcrumbsNodes = pathname.split('/') .filter((path) => !!path) .map((name, i, array) => { @@ -77,7 +108,14 @@ const TopBar = () => { {user.first_name} {user.last_name} {user.organization && ( -
{user.organization}
+
+
{user.organization}
+ {verified && ( + + + + )} +
)} {user.position && (
{user.position}
@@ -86,7 +124,7 @@ const TopBar = () => {
{t('projects')} diff --git a/src/components/pages/payment/ProjectDetail.jsx b/src/components/pages/payment/ProjectDetail.jsx index b272cede..fb334b8f 100644 --- a/src/components/pages/payment/ProjectDetail.jsx +++ b/src/components/pages/payment/ProjectDetail.jsx @@ -6,7 +6,7 @@ import TabContentBlock from 'components/pages/profile/TabContentBlock'; import { BooleanInput, Button, TextInput } from 'components/form-components'; import { Copy, RefreshCcw, HelpCircle, - Briefcase, X, + Briefcase, X, Check, Trash, Trash2, } from 'react-feather'; import Tooltip from 'components/Tooltip'; import { BlankModal, YesNoModal, DeleteModal } from 'components/modals'; @@ -20,26 +20,32 @@ import { p2sStatus, u2pRole, u2pStatus } from 'const/projects'; import toast from 'utils/toast'; import moment from 'moment'; import { useDOCookies } from 'hooks'; +import { useSelector } from 'react-redux'; const ProjectDetail = (props) => { const { match, history } = props; - const projectId = match.params.id; + const { t } = useTranslation(); const setCookie = useDOCookies()[1]; const addUserModalRef = useRef(); const refreshTokenModalRef = useRef(); + const leaveOrganizationModalRef = useRef(); const disableUserModalRef = useRef(); const deleteUserModalRef = useRef(); const disableProjectModalRef = useRef(); const updateProjectModalRef = useRef(); const removeFutureModalRef = useRef(); + const user = useSelector((store) => store.user); const [project, setProject] = useState({}); const [selectedUser, setSelectedUser] = useState({}); const [selectedSubscription, setSelectedSubscription] = useState({}); const [showInvitations, setShowInvitations] = useState(false); + const [verified, setVerified] = useState(false); + const [invitations, setInvitations] = useState([]); + const [projectId, setProjectId] = useState(match.params.id); const hasFutureSubscription = !!project.subscriptions?.find((s) => s.status === p2sStatus.FUTURE); @@ -53,6 +59,30 @@ const ProjectDetail = (props) => { Api.get(`payment/project/${projectId}/`) .then((resp) => { setProject(resp.data); + if (resp.data.verified) { + setVerified(true); + } + }); + Api.get('payment/invitations/') + .then((resp) => { + setInvitations(resp.data); + }); + }; + + const confirmInvitation = (invite) => { + Api.post(`payment/project/${invite.project_id}/confirm-invite/`) + .then(() => { + toast('success', t('invitationConfirmed')); + setProjectId(invite.project_id); + fetchData(); + }); + }; + + const rejectInvitation = (invite) => { + Api.delete(`payment/project/${invite.project_id}/reject-invite/`) + .then(() => { + toast('warning', t('invitationRejected')); + fetchData(); }); }; @@ -90,6 +120,7 @@ const ProjectDetail = (props) => { detail_logging: Yup.boolean(), }), onSubmit: (values, actions) => { + console.log(values); Api.put(`payment/project/${projectId}/update/`, values) .then(() => { toast('success', t('projectUpdated')); @@ -107,13 +138,10 @@ const ProjectDetail = (props) => { }, []); const getProjectStatus = () => { - if (project.is_default) { + if (project.is_owner) { return t('defaultProject'); } - if (project.is_active) { - return t('active'); - } - return t('deactivated'); + return t('active'); }; const getUserStatus = (userProject) => { @@ -144,6 +172,20 @@ const ProjectDetail = (props) => { }); }; + const leaveOrganization = () => { + console.log('Leave'); + Api.delete(`payment/project/${projectId}/self-delete/${user.id}/`) + .then(() => { + toast('warning', t('userWasDeleted')); + leaveOrganizationModalRef.current.hide(); + fetchData(); + }); + }; + + const openLeaveOrganizationModal = () => { + leaveOrganizationModalRef.current.show(); + }; + const deactivateUser = () => { Api.delete(`payment/project/${projectId}/deactivate-user/${selectedUser.id}/`) .then(() => { @@ -172,7 +214,7 @@ const ProjectDetail = (props) => { const toggleUserActive = (user) => { if (user.status === u2pStatus.ACTIVE) { setSelectedUser(user); - disableUserModalRef.current.show(); + //disableUserModalRef.current.show(); } else { activateUser(user.id); } @@ -280,23 +322,25 @@ const ProjectDetail = (props) => { <> {t('status')}: {getProjectStatus()} - {project.is_default && ( + {project.is_owner && ( )} + {!project.is_owner && ( + + + + )} {project.is_owner && ( <> - {!project.is_default && ( - - )}
-

- {project.name} -

+
+

+ {project.name} +

+ {project.verified && ( + + + + )} +
{t('created')}: {renderDate(project.created_at)}
@@ -469,7 +531,7 @@ const ProjectDetail = (props) => { {t('firstName')} Email - {t('status')} + {t('deleteUserFromOrganization')} @@ -478,9 +540,14 @@ const ProjectDetail = (props) => {
{user.name} +
+ + {user.email} + +
{user.role === u2pRole.OWNER && ( - + )} {user.role !== u2pRole.OWNER && project.is_owner && ( @@ -493,21 +560,6 @@ const ProjectDetail = (props) => { )}
- {user.email} - -
- {user.role !== u2pRole.OWNER && project.is_owner && ( - toggleUserActive(user)} - /> - )} - {getUserStatus(user)} -
- ))} @@ -663,6 +715,49 @@ const ProjectDetail = (props) => { )} + {!!invitations.length && project.is_owner && ( +
+

+ {t('invitation')} +

+ + + + + + + + + + {invitations.map((invite) => ( + + + + + + + ))} + +
{t('projectName')}{t('dateOfInvitation')}{t('projectOwner')} +
{invite.project_name}{renderDate(invite.updated_at)}{invite.project_owner} + + +
+
+ )} ); diff --git a/src/components/pages/profile/ProfilePage.jsx b/src/components/pages/profile/ProfilePage.jsx index 282f5b05..b73826e4 100644 --- a/src/components/pages/profile/ProfilePage.jsx +++ b/src/components/pages/profile/ProfilePage.jsx @@ -2,7 +2,7 @@ import React, { useEffect, useState } from 'react'; import Api from 'api'; import { ReactRouterPropTypes } from 'utils/prop-types'; import { - Mail, Settings, User, Clipboard, File, + Mail, Settings, User, Clipboard, File, HelpCircle, ChevronDown, Check, } from 'react-feather'; import { NavLink, Redirect, Route, Switch } from 'react-router-dom'; import { useSelector } from 'react-redux'; @@ -11,11 +11,38 @@ import ProjectsPage from '../payment/ProjectsPage'; import InvoicesTable from '../payment/InvoicesTable'; import ProfileSettings from './ProfileSettings'; import LogTable from '../payment/LogTable'; +import Tooltip from '../../Tooltip'; const ProfilePage = ({ match }) => { const { t } = useTranslation(); const [stats, setStats] = useState({ api_requests: '---', endpoints: '---' }); + const [projects, setProjects] = useState([]); + const [verified, setVerified] = useState(false); + + const fetchData = () => { + Api.get('payment/project/') + .then((resp) => { + setProjects(resp.data); + }); + }; + + const projectLink = (projects) => { + let link = '/system/profile/projects/'; + projects.forEach((project) => { + if (project.is_default) { + link = link.concat(project.id); + if (project.verified) { + setVerified(true); + } + } + }); + return link; + }; + + useEffect(() => { + fetchData(); + }, []); const user = useSelector((store) => store.user); useEffect(() => { @@ -47,7 +74,18 @@ const ProfilePage = ({ match }) => {
{user.email}
- {user.organization &&
{user.organization}
} + {user.organization && ( +
+ {verified && ( + + + + )} +
+ {user.organization} +
+
+ )} {user.position &&
{user.position}
} @@ -97,8 +135,9 @@ const ProfilePage = ({ match }) => { {/*>*/} {/* {t('profile')}*/} {/**/} + {/* eslint-disable-next-line array-callback-return */} projectLink(projects)} data-toggle="tab" className="py-4 sm:mr-8 flex items-center" activeClassName="active"