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')}
+
+
+
+
+ | {t('projectName')} |
+ {t('dateOfInvitation')} |
+ {t('projectOwner')} |
+ |
+
+
+
+ {invitations.map((invite) => (
+
+ | {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"