Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/organization logo #1041

Open
wants to merge 37 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 34 commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
097c13e
feat: fetch logo url from api
spomberg Mar 1, 2023
3ed7be0
feat: implement logoURL state
spomberg Mar 1, 2023
d8e5bda
feat: import and implement useEffect to fetch logo url
spomberg Mar 1, 2023
6866356
feat: change logo alt to logo
spomberg Mar 1, 2023
8f4b35f
feat: use ternary operator to change the logo alt attribute
spomberg Mar 1, 2023
7070022
feat: make useEffect only run if user is logged in and has an organiz…
spomberg Mar 2, 2023
892f8df
feat: test that the greenstand logo shows when the user does not belo…
spomberg Mar 2, 2023
d8850ff
feat: move logo URL state and useEffect from IconLogo component AppCo…
spomberg Mar 2, 2023
a2ceb57
feat: rename logoURL state to logoPath
spomberg Mar 2, 2023
5d233be
feat: implement isVisible function that hides logo if URL is not avai…
spomberg Mar 3, 2023
5743b23
feat: refactor isVisible function to hide greenstand logo if user wit…
spomberg Mar 3, 2023
5204b0c
feat: add comments
spomberg Mar 3, 2023
120e9c5
feat: refactor isInvisible function to not hide the greenstand logo i…
spomberg Mar 3, 2023
f844cc6
feat: fetch logo url from api
spomberg Mar 1, 2023
369b102
feat: implement logoURL state
spomberg Mar 1, 2023
3baf925
feat: import and implement useEffect to fetch logo url
spomberg Mar 1, 2023
917485a
feat: change logo alt to logo
spomberg Mar 1, 2023
53fd8ff
feat: use ternary operator to change the logo alt attribute
spomberg Mar 1, 2023
f1c470a
feat: make useEffect only run if user is logged in and has an organiz…
spomberg Mar 2, 2023
942bddd
feat: test that the greenstand logo shows when the user does not belo…
spomberg Mar 2, 2023
e6b0fd4
feat: move logo URL state and useEffect from IconLogo component AppCo…
spomberg Mar 2, 2023
6bd16f9
feat: rename logoURL state to logoPath
spomberg Mar 2, 2023
b51bb1a
feat: implement isVisible function that hides logo if URL is not avai…
spomberg Mar 3, 2023
0042adf
feat: refactor isVisible function to hide greenstand logo if user wit…
spomberg Mar 3, 2023
c4d03ac
feat: add comments
spomberg Mar 3, 2023
72655e2
feat: refactor isInvisible function to not hide the greenstand logo i…
spomberg Mar 3, 2023
c6f95fd
feat: fix setLogoPath name typo
spomberg Mar 4, 2023
6fef96f
feat: implement get request for logo url at login if user belongs to …
spomberg Mar 4, 2023
0f73aee
feat: use axios instead of fetch to retrieve the logo URL
spomberg Mar 4, 2023
faba887
feat: change response to res on logo url axios call
spomberg Mar 4, 2023
b62a881
feat: remove login from logo url useEffect dependency
spomberg Mar 4, 2023
ff3400a
feat: remove IconLogo component from login page and use the svg file …
spomberg Mar 5, 2023
63f709e
feat: implement getStakeholder(id) function
spomberg Mar 5, 2023
c148f1d
feat: call getStakeholder(id) api function to retrieve logo url
spomberg Mar 5, 2023
8fd5700
feat: add cypress.env.json
spomberg Mar 5, 2023
3c80d22
feat: create cypress.env file to store credentials to be used in inte…
spomberg Mar 5, 2023
81cd029
test: remove credentials and use cypress.env instead
spomberg Mar 5, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions cypress/integration/IconLogo.spec.py.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
describe('Icon Logo', () => {
Cypress.on('uncaught:exception', (err, runnable) => {
// returning false here prevents Cypress from
// failing the tests due to uncaught errors
return false;
});

it('Displays the organization logo when the user belongs to an organization', () => {
cy.visit('http://localhost:3001/login');
cy.get('#userName').type('test1');
cy.get('#password').type('EoCAyCPpW0');
cy.contains(/log/i).click();
cy.contains(/earnings/i).click();
cy.wait(100);
cy.get('img').should('have.attr', 'alt', 'organization logo');
});
it('Displays the Greenstand logo when the user does not belongs to an organization', () => {
cy.visit('http://localhost:3001/login');
cy.get('#userName').type('admin');
cy.get('#password').type('8pzPdcZAG6&Q');
nmcharlton marked this conversation as resolved.
Show resolved Hide resolved
cy.contains(/log/i).click();
cy.contains(/earnings/i).click();
cy.wait(100);
cy.get('img').should('have.attr', 'alt', 'greenstand logo');
});
});
18 changes: 18 additions & 0 deletions src/api/stakeholders.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,24 @@ export default {
}
},

// Returns a single stakeholder data
getStakeholder(id) {
try {
const query = `${STAKEHOLDER_API}/stakeholders/${id}`;

const options = {
method: 'GET',
headers: {
'content-type': 'application/json',
},
};

return fetchJSON(query, options);
} catch (e) {
log.error('getStakeholder', e);
}
},

deleteStakeholder(id, stakeholderData) {
try {
const orgId = id || getOrganizationId();
Expand Down
41 changes: 33 additions & 8 deletions src/components/IconLogo.js
Original file line number Diff line number Diff line change
@@ -1,22 +1,47 @@
import { Link } from 'react-router-dom';
import logo from './images/logo.svg';
import { AppContext } from '../context/AppContext';
import { React, useContext } from 'react';

/*
* Just a logo icon
*/
import React from 'react';

export default function IconLogo() {
const appContext = useContext(AppContext);
const { logoPath, user } = appContext;

// Hide logo if the logo URL hasn't been loaded or if the Greenstand logo is loaded
// and the user has an organization
function isVisible() {
if (!user) {
return 'visible';
}
if (logoPath === '') {
return 'hidden';
} else return 'visible';
}

// Logo styling objects for both org and Greenstand logos to be applied to img
const greenstandLogoStyle = {
maxWidth: 149,
maxHeight: 32,
marginBottom: '-6px',
visibility: isVisible(),
};

const orgLogoStyle = {
maxHeight: 50,
marginBottom: '-15px',
visibility: isVisible(),
};

return (
<Link to="/">
<img
style={{
maxWidth: 149,
maxHeight: 32,
marginBottom: '-6px',
}}
src={logo}
alt="Greenstand logo"
style={logoPath === logo ? greenstandLogoStyle : orgLogoStyle}
src={logoPath}
alt={logoPath === logo ? 'greenstand logo' : 'organization logo'}
/>
</Link>
);
Expand Down
29 changes: 26 additions & 3 deletions src/components/Login.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,11 @@ import {
Container,
CircularProgress,
} from '@material-ui/core';
import IconLogo from './IconLogo';
import logo from './images/logo.svg';
import { withStyles } from '@material-ui/core/styles';
import { AppContext } from '../context/AppContext';
import classNames from 'classnames';
import api from '../api/stakeholders';
import { useHistory, useLocation } from 'react-router-dom';
import axios from 'axios';
// import Copyright from 'components/Copyright'
Expand Down Expand Up @@ -142,7 +143,21 @@ const Login = (props) => {
if (res.status === 200) {
const token = res.data.token;
const user = res.data.user;
appContext.login(user, token, isRemember);
// GET logo URL from API if user belongs to an organization
// and apply it to logoPath state before completing login
if (user.policy.organization) {
const orgID = user.policy.organization.id;
try {
await api.getStakeholder(orgID).then((response) => {
const orgLogo = response.stakeholders[0].logo_url;
orgLogo && appContext.setLogoPath(orgLogo);
});
} catch (e) {
console.error('Undefined User error:', e);
} finally {
appContext.login(user, token, isRemember);
}
} else appContext.login(user, token, isRemember);
} else {
setErrorMessage('Invalid username or password');
setLoading(false);
Expand All @@ -167,7 +182,15 @@ const Login = (props) => {
<Container component="main" maxWidth="xs">
<CssBaseline />
<div className={classes.paper}>
<IconLogo />
<img
style={{
maxWidth: 149,
maxHeight: 32,
marginBottom: '-6px',
}}
src={logo}
alt="Greenstand logo"
/>
<Box m={2} />
<Typography variant="h2">Admin Panel</Typography>
<form className={classes.form} onSubmit={handleSubmit} noValidate>
Expand Down
21 changes: 21 additions & 0 deletions src/context/AppContext.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,11 @@ import CompareIcon from '@material-ui/icons/Compare';
import CreditCardIcon from '@material-ui/icons/CreditCard';
import InboxRounded from '@material-ui/icons/InboxRounded';
import MapIcon from '@material-ui/icons/Map';
import logo from '../components/images/logo.svg';
import AccountTreeIcon from '@material-ui/icons/AccountTree';
import { session, hasPermission, POLICIES } from '../models/auth';
import api from '../api/treeTrackerApi';
import stakeholdersAPI from '../api/stakeholders';
import RegionsView from 'views/RegionsView';
import log from 'loglevel';

Expand Down Expand Up @@ -220,6 +222,7 @@ export const AppProvider = (props) => {
const [userHasOrg, setUserHasOrg] = useState(false);
const [orgList, setOrgList] = useState([]);
const [orgId, setOrgId] = useState(undefined);
const [logoPath, setLogoPath] = useState('');

// TODO: The below `selectedFilters` state would be better placed under a
// separate FilterContext in the future iterations when the need to share
Expand All @@ -241,6 +244,22 @@ export const AppProvider = (props) => {
}
}, [orgList]);

// Gets organization logo url from the API
useEffect(() => {
if (user && user.policy.organization) {
const orgID = user.policy.organization.id;
try {
stakeholdersAPI.getStakeholder(orgID).then((response) => {
const orgLogo = response.stakeholders[0].logo_url;
orgLogo && setLogoPath(orgLogo);
});
} catch (e) {
console.error('Undefined User error:', e);
setLogoPath(logo);
}
} else setLogoPath(logo);
}, [user]);

function checkSession() {
const localToken = JSON.parse(localStorage.getItem('token'));
const localUser = JSON.parse(localStorage.getItem('user'));
Expand Down Expand Up @@ -339,6 +358,8 @@ export const AppProvider = (props) => {
routes,
orgId,
orgList,
logoPath,
setLogoPath,
userHasOrg,
selectedFilters,
updateSelectedFilter,
Expand Down