Skip to content
Snippets Groups Projects
Commit e5d14417 authored by Martin Lowe's avatar Martin Lowe :flag_ca:
Browse files

Merge branch 'dev' into 'master'

Merge development into master for 1.3.2 release

See merge request !460
parents 619677f7 be309639
No related branches found
No related tags found
2 merge requests!466Merge master into dev,!460Merge development into master for 1.3.2 release
Pipeline #1727 passed with stage
in 0 seconds
Showing
with 1034 additions and 767 deletions
......@@ -47,7 +47,7 @@ services:
- keycloak
- foundationdb
foundationdb:
image: eclipsefdn/foundationdb-api:staging-d8e9371-9
image: eclipsefdn/foundationdb-api:staging-10b33ae-11
ports:
- '8095:8095'
environment:
......
......@@ -134,6 +134,13 @@
<version>${openhtml.version}</version>
</dependency>
<!-- Image rescaling - better results than vanilla -->
<dependency>
<groupId>org.imgscalr</groupId>
<artifactId>imgscalr-lib</artifactId>
<version>4.2</version>
</dependency>
<!-- Annotation preprocessors - reduce all of the boiler plate -->
<dependency>
<groupId>com.google.auto.value</groupId>
......@@ -261,7 +268,7 @@
<artifactId>maven-surefire-plugin</artifactId>
<version>${surefire-plugin.version}</version>
<configuration>
<skipTests>false</skipTests>
<skipTests>false</skipTests>
<systemPropertyVariables>
<java.util.logging.manager>org.jboss.logmanager.LogManager</java.util.logging.manager>
<maven.home>${maven.home}</maven.home>
......
......@@ -153,19 +153,27 @@ public class OrganizationResource extends AbstractRESTResource {
@RolesAllowed({ CR, DE, CRA, MA })
@Path("{orgID:\\d+}")
public Response update(@PathParam("orgID") String organizationID, OrganizationInfoUpdateRequest updateRequest) {
if (updateRequest.getDescription().length() > 700) {
return new org.eclipsefoundation.core.model.Error(400, "Organization description should not be over 700 characters").asResponse();
}
// get ref and update the object
OrganizationInformation infoRef = eclipseDBDao.getReference(Integer.valueOf(organizationID),
OrganizationInformation.class);
if (infoRef == null) {
return Response.status(404).build();
MultivaluedMap<String, String> params = new MultivaluedMapImpl<>();
params.add(DefaultUrlParameterNames.ID.getName(), organizationID);
List<OrganizationInformation> infoRefs = eclipseDBDao
.get(new RDBMSQuery<>(wrap, filters.get(OrganizationInformation.class), params));
// if ref doesn't exist, create one
OrganizationInformation infoRef;
if (infoRefs.isEmpty()) {
infoRef = new OrganizationInformation();
infoRef.setOrganizationID(Integer.valueOf(organizationID));
} else {
infoRef = infoRefs.get(0);
}
infoRef.setCompanyUrl(updateRequest.getCompanyUrl());
infoRef.setShortDescription(updateRequest.getDescription());
infoRef.setLongDescription(updateRequest.getDescription());
// create the param map and update the org info
MultivaluedMap<String, String> params = new MultivaluedMapImpl<>();
params.add(DefaultUrlParameterNames.ID.getName(), organizationID);
// update the org info
List<OrganizationInformation> updatedOrg = eclipseDBDao.add(
new RDBMSQuery<>(wrap, filters.get(OrganizationInformation.class), params), Arrays.asList(infoRef));
if (updatedOrg.isEmpty()) {
......
......@@ -73,7 +73,7 @@ public interface ImageStoreService {
String webRoot();
@WithDefault("65000")
@WithDefault("1000000")
long maxSizeInBytes();
@WithDefault("")
......@@ -94,6 +94,9 @@ public interface ImageStoreService {
@WithDefault("true")
boolean enabled();
@WithDefault("200")
int maxDimension();
@WithDefault("0.80f")
@Max(1)
float factor();
......
......@@ -39,10 +39,12 @@ import javax.imageio.stream.ImageOutputStream;
import javax.inject.Inject;
import javax.ws.rs.BadRequestException;
import javax.ws.rs.ServerErrorException;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Response;
import org.apache.commons.lang3.StringUtils;
import org.eclipsefoundation.core.model.RequestWrapper;
import org.eclipsefoundation.core.namespace.DefaultUrlParameterNames;
import org.eclipsefoundation.eclipsedb.dao.EclipseDBPersistenceDAO;
import org.eclipsefoundation.eclipsedb.dto.OrganizationInformation;
import org.eclipsefoundation.persistence.model.RDBMSQuery;
......@@ -50,6 +52,8 @@ import org.eclipsefoundation.persistence.service.FilterService;
import org.eclipsefoundation.react.helper.ImageFileHelper;
import org.eclipsefoundation.react.namespace.ImageStoreFormat;
import org.eclipsefoundation.react.service.ImageStoreService;
import org.imgscalr.Scalr;
import org.jboss.resteasy.specimpl.MultivaluedMapImpl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
......@@ -142,33 +146,24 @@ public class DefaultImageStoreService implements ImageStoreService {
Path p = imageStoreRoot.resolve(ImageFileHelper.getFileNameWithExtension(fileName, format, mimeType));
BasicFileAttributeView attrView = Files.getFileAttributeView(p, BasicFileAttributeView.class);
try {
// for images bigger than the current max that don't already exist, refuse to write them
byte[] bytes = imageBytes.get();
if (bytes == null) {
LOGGER.warn("Could not generate image file with name {} and format {}, no image passed", fileName,
format);
// cannot provide image, return null
return null;
} else if (bytes.length > config.maxSizeInBytes() && (!Files.exists(p)
|| !approximatelyMatch((long) bytes.length, attrView.readAttributes().size(), 1000))) {
}
// compress image and then compare max size
byte[] compressedImage = compress(bytes, mimeType);
if (compressedImage.length > config.maxSizeInBytes() && (!Files.exists(p)
|| !approximatelyMatch((long) compressedImage.length, attrView.readAttributes().size(), 1000))) {
throw new BadRequestException(
"Passed image is larger than allowed size of '" + config.maxSizeInBytes() + "' bytes");
}
// if enabled, update the EclipseDB on logo update when image name is numeric
// max size check is related to max blob size of 64kb
// TODO remove once the Drupal API references this API as this will no longer be needed then
if (config.persistToDb() && format.isPresent()
&& ImageStoreFormat.ImageStoreFormats.WEB.equals(format.get()) && StringUtils.isNumeric(fileName)) {
// get a ref to the given organization information object
OrganizationInformation oi = dao.getReference(Integer.valueOf(fileName), OrganizationInformation.class);
// as long as this org exists, update the logo
if (oi != null) {
oi.setLargeLogo(bytes);
oi.setSmallLogo(bytes);
oi.setLargeMime(mimeType);
oi.setSmallMime(mimeType);
dao.add(new RDBMSQuery<>(new RequestWrapper(), filters.get(OrganizationInformation.class)), Arrays.asList(oi));
}
}
handleDBPersist(fileName, compressedImage, mimeType, format);
// write will create and overwrite file by default if it exists
return getWebUrl(Files.write(p, compress(bytes, mimeType)));
......@@ -214,6 +209,49 @@ public class DefaultImageStoreService implements ImageStoreService {
});
}
/**
* Handles persistence to the DB layer for images. Includes checks for size, format, and format before persisting to
* the database.
*
* @param fileName name of the file to be persisted
* @param compressedImage the compressed image bytes
* @param mimeType the mime type of the image
* @param format the format of the file
*/
private void handleDBPersist(String fileName, byte[] compressedImage, String mimeType,
Optional<ImageStoreFormat> format) {
if (config.persistToDb() && format.isPresent() && ImageStoreFormat.ImageStoreFormats.WEB.equals(format.get())
&& StringUtils.isNumeric(fileName)) {
if (compressedImage.length < 65535) {
LOGGER.warn("Cannot persist image with name {}, size of compressed image is over max size of table",
fileName);
} else {
// get a ref to the given organization information object
MultivaluedMap<String, String> params = new MultivaluedMapImpl<>();
params.add(DefaultUrlParameterNames.ID.getName(), fileName);
List<OrganizationInformation> infoRefs = dao
.get(new RDBMSQuery<>(new RequestWrapper(), filters.get(OrganizationInformation.class), params));
// if ref doesn't exist, create one
OrganizationInformation oi;
if (infoRefs.isEmpty()) {
oi = new OrganizationInformation();
oi.setOrganizationID(Integer.valueOf(fileName));
oi.setCompanyUrl("");
} else {
oi = infoRefs.get(0);
}
// as long as this org exists, update the logo
oi.setLargeLogo(compressedImage);
oi.setSmallLogo(compressedImage);
oi.setLargeMime(mimeType);
oi.setSmallMime(mimeType);
dao.add(new RDBMSQuery<>(new RequestWrapper(), filters.get(OrganizationInformation.class)),
Arrays.asList(oi));
}
}
}
/**
* Fuzzy match used to check byte size of files. Used just in case there is some minor changes to the data done via
* metadata or the like.
......@@ -256,7 +294,7 @@ public class DefaultImageStoreService implements ImageStoreService {
ImageOutputStream ios = ImageIO.createImageOutputStream(os)) {
// read in the image bytes for image io
// Note: if this fails due to an zlib exception, the image is most likely corrupt somehow
BufferedImage image = ImageIO.read(new ByteArrayInputStream(imageBytes));
BufferedImage image = rescale(ImageIO.read(new ByteArrayInputStream(imageBytes)));
// get the image writer for the current mime type
Iterator<ImageWriter> writers = ImageIO
......@@ -284,4 +322,21 @@ public class DefaultImageStoreService implements ImageStoreService {
}
}
}
/**
* Using Scalr library, uses quality-focused resizing algorithms to shrink images larger than the configured maximum
* dimension.
*
* @param original the original image to resize
* @return the resized image if larger than max dimensions, or original image
*/
private BufferedImage rescale(BufferedImage original) {
// check if we need to rescale the image to save cycles
if (original.getHeight() < config.compression().maxDimension()
&& original.getWidth() < config.compression().maxDimension()) {
return original;
}
// use Scalr to maintain ratio and resize image
return Scalr.resize(original, Scalr.Method.QUALITY, config.compression().maxDimension());
}
}
......@@ -45,7 +45,7 @@
},
"asciidoc": {
"alias": "asciidoc",
"title": " AsciiDoc® Working Group",
"title": "AsciiDoc® Working Group",
"status": "active",
"logo": "https://www.eclipse.org/org/workinggroups/assets/images/wg_asciidoc.svg",
"description": "The AsciiDoc® Working Group drives the standardization, adoption, and evolution of AsciiDoc. This group encourages and shapes the open, collaborative development of the AsciiDoc language and its processors.",
......@@ -747,7 +747,7 @@
}
]
},
"openhw-europe":{
"openhw-europe": {
"alias": "openhw-europe",
"title": "OpenHW Europe Working Group",
"status": "active",
......@@ -778,7 +778,41 @@
"description": "Guest Member"
}
]
},
"aice": {
"alias": "aice",
"title": "Eclipse AI, Cloud & Edge (AICE) Working Group",
"status": "proposed",
"logo": "https://www.eclipse.org/org/workinggroups/assets/images/wg-aice.svg",
"description": "The Eclipse AI, Cloud & Edge (AICE) Working Group will manage and operate an open lab (the “AICE OpenLab”) that will provide a set of resources to promote the advancement, implementation, and verification of open source software for AI, Cloud, and Edge computing.",
"resources": {
"charter": "https://www.eclipse.org/org/workinggroups/aice-charter.php",
"participation_agreements": {
"individual": null,
"organization": {
"document_id": "a8bc59bfcd17d5a42f1e",
"pdf": "https://www.eclipse.org/org/workinggroups/wgpa/aice-working-group-participation-agreement.pdf"
}
},
"website": "https://aice.eclipse.org/",
"members": "",
"sponsorship": "",
"contact_form": "https://accounts.eclipse.org/contact/membership"
},
"levels": [
{
"relation": "WGSD",
"description": "Strategic Member"
},
{
"relation": "WGAPS",
"description": "Participant Member"
},
{
"relation": "WGSAP",
"description": "Guest Member"
}
]
}
}
}
}
\ No newline at end of file
......@@ -19,7 +19,7 @@
"chart.js": "^3.5.0",
"concurrently": "^6.1.0",
"country-list": "^2.2.0",
"eclipsefdn-solstice-assets": "0.0.162",
"eclipsefdn-solstice-assets": "0.0.180",
"formik": "^2.2.6",
"less-watch-compiler": "^1.15.1",
"material-ui-dropzone": "^3.5.0",
......
......@@ -4,7 +4,7 @@
"id": "402880987649273c0176492a6c6200fd",
"form_id": "form_1",
"participation_level": "AsciiDoc_level_a",
"working_group_id": "ascii_doc",
"working_group": "ascii_doc",
"contact": {
"id": "111",
"form_id": "form_1",
......@@ -20,7 +20,7 @@
"id": "402880987649273c0176492a6c6300fe",
"form_id": "form_1",
"participation_level": "OpenGENESIS_level_c",
"working_group_id": "opengenesis",
"working_group": "opengenesis",
"contact": {
"id": "777",
"form_id": "form_1",
......
......@@ -19,5 +19,12 @@
"last_name": "Test",
"email": "873c6fef@ec516ea0.com",
"relations": ["DE", "MA"]
},
{
"id": "f81d34323",
"first_name": "Test",
"last_name": "Test",
"email": "dfesdfe@ec516ea0.com",
"relations": ["DE", "MA"]
}
]
......@@ -16,13 +16,13 @@ import {
getCurrentMode,
LOGIN_FROM_KEY,
MODE_REACT_ONLY,
ORIGINAL_PATH_KEY,
ROUTE_SIGN_IN,
} from './Constants/Constants';
import PortalContext from './Context/PortalContext';
import GlobalContext from './Context/GlobalContext';
import Loading from './components/UIComponents/Loading/Loading';
import { isProd } from './Utils/formFunctionHelpers';
import PortalLogin from './components/Portal/Login/PortalLogin';
import TopSlideMsg from './components/UIComponents/Notifications/TopSlideMsg';
const theme = createMuiTheme({
......@@ -158,7 +158,7 @@ const App = () => {
<Switch>
<Route exact path="/">
{localStorage.getItem(LOGIN_FROM_KEY) === 'Portal' ? (
<Redirect to="/portal" />
<Redirect to={sessionStorage.getItem(ORIGINAL_PATH_KEY) || '/portal'} />
) : (
<Redirect to="/application" />
)}
......@@ -184,22 +184,12 @@ const App = () => {
</AppTemplate>
</Route>
<Route path="/portal/login">
{/* Only show Portal when the user has a valid relation under his/her org */}
{currentUser?.relation?.length > 0 && <Redirect to="/portal" />}
<PortalLogin isFetchingUser={isFetchingUser} setIsFetchingUser={setIsFetchingUser} />
</Route>
<Route path="/portal">
{currentUser?.relation?.length ? (
<PortalContext.Provider value={PortalContextValue}>
<BrowserRouter hashType="noslash">
<Portal />
</BrowserRouter>
</PortalContext.Provider>
) : (
<Redirect to="/portal/login" />
)}
<PortalContext.Provider value={PortalContextValue}>
<BrowserRouter hashType="noslash">
<Portal isFetchingUser={isFetchingUser} setIsFetchingUser={setIsFetchingUser} />
</BrowserRouter>
</PortalContext.Provider>
</Route>
{/* Redirect user to 404 page for all the unknown pathnames/urls */}
......
......@@ -21,6 +21,7 @@ export const api_prefix = () => {
};
export const LOGIN_FROM_KEY = 'logInFrom';
export const ORIGINAL_PATH_KEY = 'originalPath';
export const API_PREFIX_FORM = api_prefix() + '/form';
export const API_FORM_PARAM = '?sort=dateCreated&order=desc';
......
......@@ -168,7 +168,7 @@ export function matchWorkingGroupFields(existingworkingGroupData, workingGroupsO
var res = [];
// Array
existingworkingGroupData.forEach((item) => {
let wg = workingGroupsOptions?.find((el) => el.label === item?.working_group_id);
let wg = workingGroupsOptions?.find((el) => el.label === item?.working_group);
const basicRepInfo = {
firstName: item?.contact?.first_name || '',
lastName: item?.contact?.last_name || '',
......@@ -180,7 +180,7 @@ export function matchWorkingGroupFields(existingworkingGroupData, workingGroupsO
workingGroup:
{
label: wg?.label,
value: item?.working_group_id,
value: item?.working_group,
participation_levels: wg?.participation_levels,
} || '',
participationLevel: item?.participation_level || '',
......@@ -291,7 +291,7 @@ export function matchWGFieldsToBackend(eachWorkingGroupData, formId) {
return {
id: eachWorkingGroupData?.id,
working_group_id: eachWorkingGroupData?.workingGroup.value,
working_group: eachWorkingGroupData?.workingGroup.value,
participation_level: eachWorkingGroupData?.participationLevel,
effective_date: theDate.toISOString().replace(/.\d+Z$/g, 'Z'),
contact: {
......@@ -737,13 +737,8 @@ export const focusOnInvalidField = () => {
};
export const fetchWrapper = (url, method, callbackFunc, dataBody, errCallbackFunc) => {
const shouldExcludeCSRF =
url.includes('https://newsroom.eclipse.org/api/resources') ||
url.includes('https://api.eclipse.org/public/member/') ||
url.includes('https://projects.eclipse.org/api/projects') ||
url.includes('https://api.eclipse.org/cbi/sponsorships');
let requestHeader = shouldExcludeCSRF ? FETCH_HEADER_WITHOUT_CSRF : FETCH_HEADER;
const shouldIncludeCSRF = url[0] === '/';
let requestHeader = shouldIncludeCSRF ? FETCH_HEADER : FETCH_HEADER_WITHOUT_CSRF;
if (url.includes('/logos') && method === 'POST') {
requestHeader = { 'x-csrf-token': FETCH_HEADER['x-csrf-token'] };
......@@ -757,7 +752,7 @@ export const fetchWrapper = (url, method, callbackFunc, dataBody, errCallbackFun
.then((res) => {
if (res.ok) {
// DELETE and 204 won't return response data, so don't do json()
return method === 'DELETE' || method === 'POST' || res.status === 204 ? res : res.json();
return method === 'DELETE' || method === 'POST' || res.status === 204 ? res : res.json();
}
throw res.status;
})
......@@ -773,8 +768,7 @@ export const fetchWrapper = (url, method, callbackFunc, dataBody, errCallbackFun
export const fetchWrapperPagination = async (url, i, callbackFunc) => {
let data = [];
const shouldIncludeCSRF = url.includes('/contacts');
const shouldIncludeCSRF = url[0] === '/';
const requestHeader = shouldIncludeCSRF ? FETCH_HEADER : FETCH_HEADER_WITHOUT_CSRF;
const getData = async () => {
......
......@@ -34,6 +34,7 @@ import { fetchWrapper, fetchWrapperPagination, isProd } from '../../../Utils/for
import GlobalContext from '../../../Context/GlobalContext';
import { checkPermission } from '../../../Utils/portalFunctionHelpers';
import HelpIcon from '@material-ui/icons/Help';
import NoAccess from '../../ErrorPages/NoAccess';
const isReactOnlyMode = getCurrentMode() === MODE_REACT_ONLY;
......@@ -492,7 +493,9 @@ export default function ContactManagement() {
saveContacts(contactData);
}, [contactData, allRelations]);
return (
return !checkPermission(PERMISSIONS_BASED_ON_ROLES.accessContacts, currentUser?.relation) ? (
<NoAccess />
) : (
<>
<RecentActorsIcon className={classes.headerIcon} />
<Typography className={classes.pageTitle} variant="h4" component="h1">
......
import {
Avatar,
Button,
Card,
CardContent,
CardMedia,
CircularProgress,
Container,
......@@ -20,8 +20,10 @@ import { useContext, useEffect, useState } from 'react';
import NoteIcon from '@material-ui/icons/Note';
import GroupIcon from '@material-ui/icons/Group';
import EmailIcon from '@material-ui/icons/Email';
import AssignmentIcon from '@material-ui/icons/Assignment';
import { OrgRep } from '../../../Interfaces/portal_interface';
import PortalContext from '../../../Context/PortalContext';
import ModalWindow from '../../UIComponents/Notifications/ModalWindow';
const useStyles = makeStyles((theme: Theme) =>
createStyles({
......@@ -59,10 +61,12 @@ const useStyles = makeStyles((theme: Theme) =>
margin: theme.spacing(0, 'auto'),
},
companyRepCard: {
position: 'relative',
backgroundColor: '#D0D0D0',
padding: theme.spacing(2, 0, 3.5),
},
repPrimary: {
fontSize: 20,
fontSize: 18,
textAlign: 'center',
},
repSecondary: {
......@@ -87,7 +91,7 @@ const useStyles = makeStyles((theme: Theme) =>
width: '100%',
},
contentItemCtn: {
padding: theme.spacing(2, 0),
padding: theme.spacing(1.5, 0),
},
contentAvatar: {
width: 35,
......@@ -104,6 +108,19 @@ const useStyles = makeStyles((theme: Theme) =>
contentItemTextSub: {
color: 'hsl(0, 0%, 75%)',
},
viewAllBtnCtn: {
position: 'absolute',
bottom: 15,
left: 15,
right: 15,
width: '100%',
display: 'flex',
justifyContent: 'end',
alignItems: 'end',
},
viewAllBtn: {
padding: theme.spacing(0.5, 1.5),
},
})
);
......@@ -112,6 +129,7 @@ export default function DashboardIntro(props: { orgRepData: Array<OrgRep> | null
const { orgRepData } = props;
const { orgInfo } = useContext(PortalContext);
const [orgIntro, setOrgIntro] = useState({ imageURL: '', name: '', website: '' });
const [open, setOpen] = useState(false);
const renderOrgRep = orgRepData?.map((rep, index) => (
<ListItem key={index}>
......@@ -155,9 +173,12 @@ export default function DashboardIntro(props: { orgRepData: Array<OrgRep> | null
</Card>
<Card className={classNames(classes.card, classes.companyRepCard)}>
<CardContent>
<List>{orgRepData ? renderOrgRep : <CircularProgress />}</List>
</CardContent>
<List>{orgRepData ? renderOrgRep?.slice(0, 3) : <CircularProgress />}</List>
<Container className={classes.viewAllBtnCtn}>
<Button className={classes.viewAllBtn} onClick={() => setOpen(true)}>
View more
</Button>
</Container>
</Card>
<Card className={classNames(classes.card, classes.companyContentCard)}>
......@@ -209,8 +230,35 @@ export default function DashboardIntro(props: { orgRepData: Array<OrgRep> | null
</Avatar>
<Container className={classes.contentItemText}>Contact Us</Container>
</ListItem>
<Divider className={classes.divider} />
<ListItem
button
component="a"
href="https://www.eclipse.org/org/documents/"
target="_blank"
rel="noopener"
className={classes.contentItemCtn}
>
<Avatar className={classes.contentAvatar}>
<AssignmentIcon />
</Avatar>
<Container className={classes.contentItemText}>Governance Documents</Container>
</ListItem>
</List>
</Card>
<ModalWindow
title="Key Representatives"
content=""
customContent={() => renderOrgRep}
handleProceed={''}
shouldOpen={open}
setShouldOpen={setOpen}
cancelText={'Close'}
yesText={false}
/>
</Container>
);
}
......@@ -33,7 +33,8 @@ export default function PortalLogin({ isFetchingUser, setIsFetchingUser }: Porta
<a href="mailto:membership.coordination@eclipse-foundation.org">
membership.coordination@eclipse-foundation.org
</a>{' '}
to have your account connected to the organization.
to have your account connected to the organization. If you recently connected your account to your
organization, it may take up to 24 hours to see the changes.
</p>
<p className="margin-bottom-30">
For more information on membership, including how to become a member, visit{' '}
......
import { createStyles, makeStyles, Typography, Theme } from '@material-ui/core';
import BusinessIcon from '@material-ui/icons/Business';
import { brightOrange, iconGray } from '../../../Constants/Constants';
import { brightOrange, iconGray, PERMISSIONS_BASED_ON_ROLES } from '../../../Constants/Constants';
import GlobalContext from '../../../Context/GlobalContext';
import { checkPermission } from '../../../Utils/portalFunctionHelpers';
import OrgProfilesBasicInfo from './OrgProfilesBasicInfo';
import OrgProfilesLinks from './OrgProfilesLinks';
import { useContext } from 'react';
import NoAccess from '../../ErrorPages/NoAccess';
const useStyle = makeStyles((theme: Theme) =>
createStyles({
......@@ -19,8 +23,11 @@ const useStyle = makeStyles((theme: Theme) =>
export default function OrgProfile() {
const classes = useStyle();
const { currentUser } = useContext(GlobalContext);
return (
return !checkPermission(PERMISSIONS_BASED_ON_ROLES.accessOrgProfile, currentUser?.relation) ? (
<NoAccess />
) : (
<>
<BusinessIcon className={classes.headerIcon} />
<Typography variant="h4" component="h1" className={classes.pageHeader}>
......
......@@ -4,16 +4,15 @@ import { Switch, Route, Redirect } from 'react-router-dom';
import { CircularProgress, Theme } from '@material-ui/core';
import { makeStyles, createStyles } from '@material-ui/core/styles';
import LeftNavBar from './NavBar/LeftNavBar';
import { LOGIN_FROM_KEY, mainContentBGColor, PERMISSIONS_BASED_ON_ROLES } from '../../Constants/Constants';
import { LOGIN_FROM_KEY, mainContentBGColor, ORIGINAL_PATH_KEY } from '../../Constants/Constants';
import Dashboard from './Dashboard/Dashboard';
import AppTopBar from './NavBar/AppTopBar';
import OrgProfile from './OrgProfile/OrgProfiles';
import ContactManagement from './ContactManagement/ContactManagement';
import { useEffect, useContext, useState } from 'react';
import GlobalContext from '../../Context/GlobalContext';
import { checkPermission } from '../../Utils/portalFunctionHelpers';
import NoAccess from '../ErrorPages/NoAccess';
import PortalFooter from './PortalFooter';
import PortalLogin from './Login/PortalLogin';
const useStyles = makeStyles((theme: Theme) =>
createStyles({
......@@ -35,63 +34,94 @@ const useStyles = makeStyles((theme: Theme) =>
},
},
loadingContainer: {
height: 'calc(100vh - 200px)',
backgroundColor: '#fff',
position: 'fixed',
top: 0,
bottom: 0,
left: 0,
right: 0,
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
},
loginFooter: {
'& div, & div a': {
color: 'white',
textShadow: '1px 1px #000',
},
},
})
);
export default function MainPortal() {
const originalPath = window.location.pathname !== '/portal/login' ? window.location.pathname : '/portal/dashboard';
export default function MainPortal({
isFetchingUser,
setIsFetchingUser,
}: {
isFetchingUser: boolean;
setIsFetchingUser: () => void;
}) {
const classes = useStyles();
const [mobileOpen, setMobileOpen] = useState(false);
const { gotCSRF, currentUser } = useContext(GlobalContext);
const loggedIn = currentUser !== null;
const handleDrawerToggle = () => {
setMobileOpen(!mobileOpen);
};
useEffect(() => {
!loggedIn
? sessionStorage.setItem(ORIGINAL_PATH_KEY, originalPath)
: sessionStorage.setItem(ORIGINAL_PATH_KEY, '/portal');
}, [loggedIn]);
useEffect(() => {
localStorage.setItem(LOGIN_FROM_KEY, 'Form');
}, []);
return (
<>
<AppTopBar handleDrawerToggle={handleDrawerToggle} />
<LeftNavBar mobileOpen={mobileOpen} handleDrawerToggle={handleDrawerToggle} />
<main className={classes.content}>
{gotCSRF ? (
<Switch>
<Route path="/portal/dashboard">
<Dashboard />
</Route>
<Route exact path="/portal/org-profile">
{checkPermission(PERMISSIONS_BASED_ON_ROLES.accessOrgProfile, currentUser?.relation) ? (
{currentUser && (
<>
<AppTopBar handleDrawerToggle={handleDrawerToggle} />
<LeftNavBar mobileOpen={mobileOpen} handleDrawerToggle={handleDrawerToggle} />
</>
)}
{/* When current user has not logged in, we need to show login page, so the main content part should be hidden */}
<main className={loggedIn ? classes.content : ''}>
{gotCSRF && !isFetchingUser ? (
<>
<Switch>
<Route path="/portal/login">
{loggedIn && <Redirect to={sessionStorage.getItem(ORIGINAL_PATH_KEY)} />}
<PortalLogin isFetchingUser={isFetchingUser} setIsFetchingUser={setIsFetchingUser} />
</Route>
<Route path="/portal/dashboard">
{!loggedIn && <Redirect to="/portal/login" />}
<Dashboard />
</Route>
<Route exact path="/portal/org-profile">
{!loggedIn && <Redirect to="/portal/login" />}
<OrgProfile />
) : (
<NoAccess />
)}
</Route>
<Route exact path="/portal/contact-management">
{checkPermission(PERMISSIONS_BASED_ON_ROLES.accessContacts, currentUser?.relation) ? (
</Route>
<Route exact path="/portal/contact-management">
{!loggedIn && <Redirect to="/portal/login" />}
<ContactManagement />
) : (
<NoAccess />
)}
</Route>
<Route path="/portal">
<Redirect to="/portal/dashboard" />
</Route>
</Switch>
</Route>
<Route path="/portal">
<Redirect to="/portal/dashboard" />
</Route>
</Switch>
<div className={classes.loginFooter}>
<PortalFooter />
</div>
</>
) : (
<div className={classes.loadingContainer}>
<CircularProgress />
</div>
)}
<PortalFooter />
</main>
</>
);
......
This diff is collapsed.
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment