From 157007683d1709949c97569a66ff493d4a37fb8c Mon Sep 17 00:00:00 2001 From: Sam Rice Date: Tue, 9 Apr 2024 20:58:22 -0700 Subject: [PATCH 01/11] feat: add grower page implementation, test file, and stub data --- .../integration/growers/[growerid].cy.js | 21 + cypress/tests/integration/nockRoutes.js | 43 +- doc/examples/growers/100.json | 16 + src/pages/growers/[growerid].js | 532 ++++++++++++++++++ 4 files changed, 610 insertions(+), 2 deletions(-) create mode 100644 cypress/tests/integration/growers/[growerid].cy.js create mode 100644 doc/examples/growers/100.json create mode 100644 src/pages/growers/[growerid].js diff --git a/cypress/tests/integration/growers/[growerid].cy.js b/cypress/tests/integration/growers/[growerid].cy.js new file mode 100644 index 00000000..8e3d3a70 --- /dev/null +++ b/cypress/tests/integration/growers/[growerid].cy.js @@ -0,0 +1,21 @@ +import grower from '../../../../doc/examples/growers/100.json'; +import { prepareNocks, clearNocks } from '../nockRoutes'; + +beforeEach(() => { + clearNocks(); +}); + +describe('Grower Page', () => { + it('renders with grower data', () => { + const path = `/growers/${grower.id}`; + prepareNocks({ grower }); + + cy.visit(path, { + failOnStatusCode: false, + }); + + cy.url().should('include', '/growers'); + cy.contains(grower.first_name); + cy.screenshot(); + }); +}); diff --git a/cypress/tests/integration/nockRoutes.js b/cypress/tests/integration/nockRoutes.js index 5a933a78..bef4d505 100644 --- a/cypress/tests/integration/nockRoutes.js +++ b/cypress/tests/integration/nockRoutes.js @@ -1,3 +1,4 @@ +import grower100 from '../../../doc/examples/growers/100.json'; import organization1 from '../../../doc/examples/organizations/1.json'; import planter940 from '../../../doc/examples/planters/940.json'; import { defaultConfig } from '../../../src/context/configContext'; @@ -10,11 +11,13 @@ export function getNockRoutes( tree: {}, organization: {}, planter: {}, + grower: {}, capture: {}, }, ) { const organization = { ...organization1, ...props.organization }; const planter = { ...planter940, ...props.planter }; + const grower = { ...grower100, ...props.grower }; const tree = { ...tree186734, ...props.tree }; const capture = { ...capture1, ...props.capture }; return [ @@ -25,6 +28,13 @@ export function getNockRoutes( body: planter, }, + { + method: 'GET', + path: `/growers/${grower.id}`, + statusCode: 200, + body: grower, + }, + { method: 'GET', path: '/trees/featured', @@ -50,9 +60,9 @@ export function getNockRoutes( { method: 'GET', - path: `/growers/${planter.id}`, + path: `/growers/${grower.grower_id}`, statusCode: 200, - body: planter, + body: grower, }, { @@ -85,6 +95,29 @@ export function getNockRoutes( }, }, + { + method: 'GET', + path: grower.links.species, + statusCode: 200, + body: { + species: [tree], + }, + }, + { + method: 'GET', + path: grower.links.associated_organizations, + statusCode: 200, + body: { organizations: [organization] }, + }, + { + method: 'GET', + path: grower.links.featured_trees, + statusCode: 200, + body: { + trees: [tree], + }, + }, + { method: 'GET', path: organization.links.species, @@ -99,6 +132,12 @@ export function getNockRoutes( statusCode: 200, body: { planters: [planter] }, }, + { + method: 'GET', + path: organization.links.associated_growers, + statusCode: 200, + body: { growers: [grower] }, + }, { method: 'GET', path: organization.links.featured_trees, diff --git a/doc/examples/growers/100.json b/doc/examples/growers/100.json new file mode 100644 index 00000000..5782fc12 --- /dev/null +++ b/doc/examples/growers/100.json @@ -0,0 +1,16 @@ +{ + "id": 100, + "first_name": "Stanley", + "last_name": "Kubrick", + "links": { + "featured_trees": "/trees?grower_id=100&limit=4", + "associated_organizations": "/organizations?grower_id=100", + "species": "/species?grower_id=100" + }, + "image_url": "https://treetracker-dev-images.s3.eu-central-1.amazonaws.com/2020.10.19.09.47.53_-5.508107173727935_38.981361706266256_39f0cc9d-0f13-4547-8142-150f15cabb67_IMG_20201019_094513_6614320100195503436.jpg", + "trees_planted": 4, + "about": "Greenway is a Youth-Driven Environmental Protection Organization providing alternative solutions to single-use plastic and planting carbon-sucking trees for socio-economic development and reducing climate crisis. Our social work includes reforestation, landscape restoration, climate education, awareness campaign, conducting research, outreach activities, and collaborating with key stakeholders to implement sustainable solutions.", + "mission": "To combat climate change, desertification, land degradation, carbon emission by inspiring healthier communities affected by severe climate disorder and modestly reducing pollution by 2050.", + "created_time": "2018-01-01", + "country": "Tanzania" +} \ No newline at end of file diff --git a/src/pages/growers/[growerid].js b/src/pages/growers/[growerid].js new file mode 100644 index 00000000..fbed70df --- /dev/null +++ b/src/pages/growers/[growerid].js @@ -0,0 +1,532 @@ +/* eslint-disable @next/next/no-img-element */ +import CheckIcon from '@mui/icons-material/Check'; +import Avatar from '@mui/material/Avatar'; +import Box from '@mui/material/Box'; +import Divider from '@mui/material/Divider'; +import Grid from '@mui/material/Grid'; +import Portal from '@mui/material/Portal'; +import Typography from '@mui/material/Typography'; +import log from 'loglevel'; +import { marked } from 'marked'; +import moment from 'moment'; +import { useRouter } from 'next/router'; +import { useEffect, useMemo, useState } from 'react'; +import Badge from 'components/Badge'; +import CustomWorldMap from 'components/CustomWorldMap'; +import FeaturedTreesSlider from 'components/FeaturedTreesSlider'; +import HeadTag from 'components/HeadTag'; +import InformationCard1 from 'components/InformationCard1'; +import ProfileAvatar from 'components/ProfileAvatar'; +import TreeSpeciesCard from 'components/TreeSpeciesCard'; +import Crumbs from 'components/common/Crumbs'; +import CustomCard from 'components/common/CustomCard'; +import Icon from 'components/common/CustomIcon'; +import Info from 'components/common/Info'; +import { useDrawerContext } from 'context/DrawerContext'; +import { useMobile } from 'hooks/globalHooks'; +import planterBackground from 'images/background.png'; +import CalendarIcon from 'images/icons/calendar.svg'; +import LocationIcon from 'images/icons/location.svg'; +import PeopleIcon from 'images/icons/people.svg'; +import TreeIcon from 'images/icons/tree.svg'; +import SearchIcon from 'images/search.svg'; +import { useMapContext } from 'mapContext'; +import { getGrowerById, getOrgLinks } from 'models/api'; +import { makeStyles } from 'models/makeStyles'; +import * as pathResolver from 'models/pathResolver'; +import { getLocationString, getPlanterName, wrapper } from 'models/utils'; + +// make styles for component with material-ui +const useStyles = makeStyles()((theme) => ({ + imageContainer: { + position: 'relative', + flexGrow: 1, + width: '100%', + marginTop: 20, + borderRadius: 16, + overflow: 'hidden', + }, + treeSlider: { + marginTop: theme.spacing(10), + }, + divider: { + marginTop: theme.spacing(20), + marginBottom: theme.spacing(20), + [theme.breakpoints.down('md')]: { + marginTop: theme.spacing(14), + marginBottom: theme.spacing(14), + }, + }, + profileImg: { + maxHeight: '764px', + borderRadius: '16px', + [theme.breakpoints.down('md')]: { + height: '332px', + }, + }, +})); + +export default function Grower(props) { + log.warn('props for grower page:', props); + const { grower, nextExtraIsEmbed } = props; + + const { featuredTrees } = grower; + const treeCount = featuredTrees?.total; + const mapContext = useMapContext(); + const isMobile = useMobile(); + + const router = useRouter(); + + const [isGrowerTab, setIsGrowerTab] = useState(true); + + const { setTitlesData } = useDrawerContext(); + + const { classes } = useStyles(); + + // try to find first tree image or default image return + const backgroundPic = + grower?.featuredTrees?.trees?.[0]?.image_url || + `${router.basePath}${planterBackground}`; + + useEffect(() => { + setTitlesData({ + firstName: grower.first_name, + lastName: grower.last_name, + createdTime: grower.created_time, + }); + }, [grower.created_time, grower.first_name, grower.last_name, setTitlesData]); + + useEffect(() => { + async function reload() { + // manipulate the map + const { map } = mapContext; + if (map && grower) { + // map.flyTo(tree.lat, tree.lon, 16); + await map.setFilters({ + userid: grower.id, + }); + const bounds = pathResolver.getBounds(router); + if (bounds) { + log.warn('goto bounds found in url'); + await map.gotoBounds(bounds); + } else { + const view = await map.getInitialView(); + map.gotoView(view.center.lat, view.center.lon, view.zoomLevel); + } + } + } + reload(); + }, [mapContext, grower]); + + const BadgeSection = useMemo( + () => ( + <> + } + badgeName="Verified Grower" + /> + + + ), + [], + ); + + return ( + <> + + [t.spacing(0, 4), 6], + width: 1, + boxSizing: 'border-box', + }, + nextExtraIsEmbed && { + padding: (t) => [t.spacing(0, 4), 4], + }, + ]} + > + {/* + + */} + {/* + + */} + {!isMobile && ( + + , + name: 'Home', + url: '/', + }, + { + icon: grower.image_url, + name: `${getPlanterName( + grower.first_name, + grower.last_name, + )}`, + }, + ]} + /> + + + + + )} + + profile + + + {isMobile && ( + document.getElementById('drawer-title-container')} + > + + + {getPlanterName(grower.first_name, grower.last_name)} + + + + Grower since + + + } + /> + + + + + + {BadgeSection} + + + + )} + {isMobile && ( + + document.getElementById('drawer-title-container-min') + } + > + + + {grower.first_name}{' '} + {grower.last_name && grower.last_name.slice(0, 1)}. + + + + )} + {!isMobile && ( + + + {grower.first_name}{' '} + {grower.last_name && grower.last_name.slice(0, 1)}. + + + + Grower since + + + } + /> + + + + + + {BadgeSection} + + + )} + + + Featured trees by {grower.first_name} + + `/growers/${grower.id}/trees/${item.id}`} + /> + + + + setIsGrowerTab(true)} + iconURI={TreeIcon} + iconProps={{ + sx: { + '& path': { + fill: ({ palette }) => palette.primary.main, + }, + }, + }} + title="Trees Planted" + text={treeCount} + disabled={!isGrowerTab} + /> + + + setIsGrowerTab(false) + : undefined + } + iconURI={PeopleIcon} + iconProps={{ + sx: { + '& path': { + fill: ({ palette }) => palette.text.primary, + }, + }, + }} + title="Associated Orgs" + text={ + grower.associatedOrganizations.organizations.length || ( + + Individual grower + + ) + } + disabled={isGrowerTab} + /> + + + + {grower.continent_name && ( + + + + )} + + Species of trees planted + + + {grower.species.species.map((species) => ( + + ))} + + {(!grower.species.species || grower.species.species.length === 0) && ( + NO DATA YET + )} + + + {grower.associatedOrganizations.organizations.map((org) => ( + <> + + + + ))} + + + + About the Grower + + + + + + + + {nextExtraIsEmbed && ( + document.getElementById('embed-logo-container')} + > + + + )} + + ); +} + +async function serverSideData(params) { + const id = params.growerid; + const grower = await getGrowerById(id); + const data = await getOrgLinks(grower.links); + return { + grower: { ...grower, ...data }, + }; +} + +const getStaticProps = wrapper(async ({ params }) => { + const props = await serverSideData(params); + return { + props, + revalidate: Number(process.env.NEXT_CACHE_REVALIDATION_OVERRIDE) || 30, + }; +}); + +// eslint-disable-next-line +const getStaticPaths = async () => { + return { + paths: [], + fallback: 'blocking', + }; +}; + +export { getStaticProps, getStaticPaths }; From 7b367d4fdf5a1476771997bf2c52d10478bb46f9 Mon Sep 17 00:00:00 2001 From: Sam Rice Date: Wed, 10 Apr 2024 11:37:36 -0700 Subject: [PATCH 02/11] fix: minor typos in grower page --- src/pages/growers/[growerid].js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pages/growers/[growerid].js b/src/pages/growers/[growerid].js index fbed70df..a00366d0 100644 --- a/src/pages/growers/[growerid].js +++ b/src/pages/growers/[growerid].js @@ -452,7 +452,7 @@ export default function Grower(props) { ))} Date: Thu, 11 Apr 2024 14:27:10 -0700 Subject: [PATCH 03/11] feat: remove/replace references to planter entities --- cypress/tests/integration/nockRoutes.js | 52 -- .../integration/planters/[planterid].cy.js | 16 - doc/examples/planters/940.json | 16 - doc/examples/planters/940/organizations.json | 0 doc/examples/planters/940/trees.json | 6 - package-lock.json | 4 +- src/mocks/handlers.js | 14 +- src/models/api.js | 14 - src/models/api.spec.js | 9 +- src/models/utils.js | 4 +- src/pages/growers/[growerid].js | 18 +- src/pages/planters/[planterid].js | 552 ------------------ src/pages/tokens/[tokenid].js | 19 +- src/pages/trees/[treeid].js | 13 +- 14 files changed, 41 insertions(+), 696 deletions(-) delete mode 100644 cypress/tests/integration/planters/[planterid].cy.js delete mode 100644 doc/examples/planters/940.json delete mode 100644 doc/examples/planters/940/organizations.json delete mode 100644 doc/examples/planters/940/trees.json delete mode 100644 src/pages/planters/[planterid].js diff --git a/cypress/tests/integration/nockRoutes.js b/cypress/tests/integration/nockRoutes.js index bef4d505..86c9b38a 100644 --- a/cypress/tests/integration/nockRoutes.js +++ b/cypress/tests/integration/nockRoutes.js @@ -1,6 +1,5 @@ import grower100 from '../../../doc/examples/growers/100.json'; import organization1 from '../../../doc/examples/organizations/1.json'; -import planter940 from '../../../doc/examples/planters/940.json'; import { defaultConfig } from '../../../src/context/configContext'; import capture1 from '../../fixtures/capture.json'; import leader from '../../fixtures/countries/leader.json'; @@ -10,31 +9,21 @@ export function getNockRoutes( props = { tree: {}, organization: {}, - planter: {}, grower: {}, capture: {}, }, ) { const organization = { ...organization1, ...props.organization }; - const planter = { ...planter940, ...props.planter }; const grower = { ...grower100, ...props.grower }; const tree = { ...tree186734, ...props.tree }; const capture = { ...capture1, ...props.capture }; return [ - { - method: 'GET', - path: `/planters/${planter.id}`, - statusCode: 200, - body: planter, - }, - { method: 'GET', path: `/growers/${grower.id}`, statusCode: 200, body: grower, }, - { method: 'GET', path: '/trees/featured', @@ -43,58 +32,24 @@ export function getNockRoutes( trees: [tree], }, }, - { method: 'GET', path: `/trees/${tree.id}`, statusCode: 200, body: tree, }, - - { - method: 'GET', - path: `/planters/${planter.planter_id}`, - statusCode: 200, - body: planter, - }, - { method: 'GET', path: `/growers/${grower.grower_id}`, statusCode: 200, body: grower, }, - { method: 'GET', path: `/organizations/${organization.id}`, statusCode: 200, body: organization, }, - - { - method: 'GET', - path: planter.links.species, - statusCode: 200, - body: { - species: [tree], - }, - }, - { - method: 'GET', - path: planter.links.associated_organizations, - statusCode: 200, - body: { organizations: [organization] }, - }, - { - method: 'GET', - path: planter.links.featured_trees, - statusCode: 200, - body: { - trees: [tree], - }, - }, - { method: 'GET', path: grower.links.species, @@ -117,7 +72,6 @@ export function getNockRoutes( trees: [tree], }, }, - { method: 'GET', path: organization.links.species, @@ -126,12 +80,6 @@ export function getNockRoutes( species: [tree], }, }, - { - method: 'GET', - path: organization.links.associated_planters, - statusCode: 200, - body: { planters: [planter] }, - }, { method: 'GET', path: organization.links.associated_growers, diff --git a/cypress/tests/integration/planters/[planterid].cy.js b/cypress/tests/integration/planters/[planterid].cy.js deleted file mode 100644 index beab1137..00000000 --- a/cypress/tests/integration/planters/[planterid].cy.js +++ /dev/null @@ -1,16 +0,0 @@ -import planter from '../../../../doc/examples/planters/940.json'; -import { prepareNocks, clearNocks } from '../nockRoutes'; - -beforeEach(() => { - clearNocks(); -}); - -describe('Planter page', () => { - it('getServerSideProps return mocks', () => { - const path = `/planters/${planter.id}`; - prepareNocks({ planter }); - cy.visit(path); - cy.get('.MuiTypography-h2').contains(/sebastian g/i); - cy.screenshot(); - }); -}); diff --git a/doc/examples/planters/940.json b/doc/examples/planters/940.json deleted file mode 100644 index 83eb9e5d..00000000 --- a/doc/examples/planters/940.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "id": 940, - "first_name": "Sebastian ", - "last_name": "Gaertner", - "image_url": "https://treetracker-dev-images.s3.eu-central-1.amazonaws.com/2020.10.19.09.47.53_-5.508107173727935_38.981361706266256_39f0cc9d-0f13-4547-8142-150f15cabb67_IMG_20201019_094513_6614320100195503436.jpg", - "trees_planted": 4, - "about": "Greenway is a Youth-Driven Environmental Protection Organization providing alternative solutions to single-use plastic and planting carbon-sucking trees for socio-economic development and reducing climate crisis. Our social work includes reforestation, landscape restoration, climate education, awareness campaign, conducting research, outreach activities, and collaborating with key stakeholders to implement sustainable solutions.", - "mission": "To combat climate change, desertification, land degradation, carbon emission by inspiring healthier communities affected by severe climate disorder and modestly reducing pollution by 2050.", - "created_time": "2018-01-01", - "country": "Tanzania", - "links": { - "featured_trees": "/trees?planter_id=940&limit=4", - "associated_organizations": "/organizations?planter_id=940", - "species": "/species?planter_id=940" - } -} diff --git a/doc/examples/planters/940/organizations.json b/doc/examples/planters/940/organizations.json deleted file mode 100644 index e69de29b..00000000 diff --git a/doc/examples/planters/940/trees.json b/doc/examples/planters/940/trees.json deleted file mode 100644 index 72c518aa..00000000 --- a/doc/examples/planters/940/trees.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "summary": { - "total": "0" - }, - "trees": [{}] -} diff --git a/package-lock.json b/package-lock.json index 07d05357..6d9ea7bf 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "client", - "version": "2.6.0-v2.5", + "version": "2.6.0-v2.9", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "client", - "version": "2.6.0-v2.5", + "version": "2.6.0-v2.9", "dependencies": { "@emotion/cache": "^11.5.0", "@emotion/react": "^11.5.0", diff --git a/src/mocks/handlers.js b/src/mocks/handlers.js index 0f9e4723..a9f16733 100644 --- a/src/mocks/handlers.js +++ b/src/mocks/handlers.js @@ -1,8 +1,8 @@ import { rest } from 'msw'; import mockTree from '../../cypress/fixtures/tree186734.json'; import leader from '../../doc/examples/countries/leader.json'; +import grower from '../../doc/examples/growers/100.json'; import organization from '../../doc/examples/organizations/1.json'; -import planter from '../../doc/examples/planters/940.json'; import species from '../../doc/examples/species/1.json'; const trees = { trees: [mockTree, mockTree, mockTree] }; @@ -20,18 +20,20 @@ const handlers = [ rest.get(`${host}/trees*`, (req, res, ctx) => res(ctx.status(200, 'success'), ctx.json(trees)), ), - rest.get(`${host}/planters/featured`, (req, res, ctx) => + + rest.get(`${host}/growers/featured`, (req, res, ctx) => res( ctx.status(200, 'success'), ctx.json({ - planters: [planter, planter, planter], + growers: [grower, grower, grower], }), ), ), - rest.get(`${host}/planters/:id`, (req, res, ctx) => - res(ctx.status(200, 'success'), ctx.json(planter)), + rest.get(`${host}/growers/:id`, (req, res, ctx) => + res(ctx.status(200, 'success'), ctx.json(grower)), ), + rest.get(`${host}/organizations/featured`, (req, res, ctx) => res( ctx.status(200, 'success'), @@ -40,6 +42,7 @@ const handlers = [ }), ), ), + rest.get(`${host}/organizations/:id`, (req, res, ctx) => res(ctx.status(200, 'success'), ctx.json(organization)), ), @@ -57,4 +60,5 @@ const handlers = [ res(ctx.status(200, 'success'), ctx.json(leader)), ), ]; + export default handlers; diff --git a/src/models/api.js b/src/models/api.js index 817e4a80..3a53a5ec 100644 --- a/src/models/api.js +++ b/src/models/api.js @@ -101,20 +101,6 @@ export async function getCountryByLatLon(lat, lon) { } } -export async function getPlanterById(id) { - try { - const url = apiPaths.planters(id); - const begin = Date.now(); - const res = await axios.get(url); - const data = await res.data; - log.warn('url:', url, 'took:', Date.now() - begin); - return data; - } catch (err) { - log.error(err.message); - throw err; - } -} - export async function getGrowerById(id) { try { const url = apiPaths.growers(id); diff --git a/src/models/api.spec.js b/src/models/api.spec.js index 5b33e895..d5c956b4 100644 --- a/src/models/api.spec.js +++ b/src/models/api.spec.js @@ -4,11 +4,16 @@ import { getFeaturedTrees, getOrganizationById, getOrgLinks, - getPlanterById, + getGrowerById, getTreeById, } from './api'; import organization from '../../doc/examples/organizations/1.json'; -import mockPlanter from '../../doc/examples/planters/940.json'; + +it('should get grower by id', async () => { + const grower = await getGrowerById(100); + expect(grower).toBeDefined(); + expect(grower.first_name).toBeDefined(); +}); it('should get featured trees', async () => { const trees = await getFeaturedTrees(); diff --git a/src/models/utils.js b/src/models/utils.js index dbb26ff0..7b77bdca 100644 --- a/src/models/utils.js +++ b/src/models/utils.js @@ -198,7 +198,7 @@ const optimizeThemeFonts = (theme) => { return temp; }; -function getPlanterName(firstName, lastName) { +function getGrowerName(firstName, lastName) { return `${firstName} ${(lastName && lastName.slice(0, 1)) || ''}`; } @@ -322,7 +322,7 @@ export { debounce, loadFonts, optimizeThemeFonts, - getPlanterName, + getGrowerName, convertFontObjToFontArr, nextPathBaseDecode, nextPathBaseEncode, diff --git a/src/pages/growers/[growerid].js b/src/pages/growers/[growerid].js index a00366d0..6e962385 100644 --- a/src/pages/growers/[growerid].js +++ b/src/pages/growers/[growerid].js @@ -24,7 +24,7 @@ import Icon from 'components/common/CustomIcon'; import Info from 'components/common/Info'; import { useDrawerContext } from 'context/DrawerContext'; import { useMobile } from 'hooks/globalHooks'; -import planterBackground from 'images/background.png'; +import growerBackground from 'images/background.png'; import CalendarIcon from 'images/icons/calendar.svg'; import LocationIcon from 'images/icons/location.svg'; import PeopleIcon from 'images/icons/people.svg'; @@ -34,7 +34,7 @@ import { useMapContext } from 'mapContext'; import { getGrowerById, getOrgLinks } from 'models/api'; import { makeStyles } from 'models/makeStyles'; import * as pathResolver from 'models/pathResolver'; -import { getLocationString, getPlanterName, wrapper } from 'models/utils'; +import { getLocationString, getGrowerName, wrapper } from 'models/utils'; // make styles for component with material-ui const useStyles = makeStyles()((theme) => ({ @@ -86,7 +86,7 @@ export default function Grower(props) { // try to find first tree image or default image return const backgroundPic = grower?.featuredTrees?.trees?.[0]?.image_url || - `${router.basePath}${planterBackground}`; + `${router.basePath}${growerBackground}`; useEffect(() => { setTitlesData({ @@ -135,10 +135,7 @@ export default function Grower(props) { return ( <> @@ -228,7 +222,7 @@ export default function Grower(props) { }} > - {getPlanterName(grower.first_name, grower.last_name)} + {getGrowerName(grower.first_name, grower.last_name)} ({ - imageContainer: { - position: 'relative', - flexGrow: 1, - width: '100%', - marginTop: 20, - borderRadius: 16, - overflow: 'hidden', - }, - treeSlider: { - marginTop: theme.spacing(10), - }, - divider: { - marginTop: theme.spacing(20), - marginBottom: theme.spacing(20), - [theme.breakpoints.down('md')]: { - marginTop: theme.spacing(14), - marginBottom: theme.spacing(14), - }, - }, - profileImg: { - maxHeight: '764px', - borderRadius: '16px', - [theme.breakpoints.down('md')]: { - height: '332px', - }, - }, -})); - -const placeholderText = `Lorem ipsum dolor sit amet consectetur adipisicing elit. Culpa iusto - nesciunt quasi praesentium non cupiditate ratione nihil. Perferendis, - velit ipsa illo, odit unde atque doloribus tempora distinctio facere - dolorem expedita error. Natus, provident. Tempore harum repellendus - reprehenderit vitae temporibus, consequuntur blanditiis officia - excepturi, natus explicabo laborum delectus repudiandae placeat - eligendi.`; -export default function Planter(props) { - log.warn('props for planter page:', props); - const { planter, nextExtraIsEmbed } = props; - - const { featuredTrees } = planter; - const treeCount = featuredTrees?.total; - const mapContext = useMapContext(); - const isMobile = useMobile(); - - const router = useRouter(); - - const [isPlanterTab, setIsPlanterTab] = useState(true); - - const { setTitlesData } = useDrawerContext(); - - const { classes } = useStyles(); - - // try to find first tree image or default image return - const backgroundPic = - planter?.featuredTrees?.trees?.[0]?.image_url || - `${router.basePath}${planterBackground}`; - - useEffect(() => { - setTitlesData({ - firstName: planter.first_name, - lastName: planter.last_name, - createdTime: planter.created_time, - }); - }, [ - planter.created_time, - planter.first_name, - planter.last_name, - setTitlesData, - ]); - - useEffect(() => { - async function reload() { - // manipulate the map - const { map } = mapContext; - if (map && planter) { - // map.flyTo(tree.lat, tree.lon, 16); - await map.setFilters({ - userid: planter.id, - }); - const bounds = pathResolver.getBounds(router); - if (bounds) { - log.warn('goto bounds found in url'); - await map.gotoBounds(bounds); - } else { - const view = await map.getInitialView(); - map.gotoView(view.center.lat, view.center.lon, view.zoomLevel); - } - } - } - reload(); - }, [mapContext, planter]); - - const BadgeSection = useMemo( - () => ( - <> - } - badgeName="Verified Planter" - /> - - - ), - [], - ); - - return ( - <> - - [t.spacing(0, 4), 6], - width: 1, - boxSizing: 'border-box', - }, - nextExtraIsEmbed && { - padding: (t) => [t.spacing(0, 4), 4], - }, - ]} - > - {/* - - */} - {/* - - */} - {!isMobile && ( - - , - name: 'Home', - url: '/', - }, - { - icon: planter.image_url, - name: `${getPlanterName( - planter.first_name, - planter.last_name, - )}`, - }, - ]} - /> - - - - - )} - - - profile - - - {isMobile && ( - document.getElementById('drawer-title-container')} - > - - - {getPlanterName(planter.first_name, planter.last_name)} - - - - Planter since - - - } - /> - - - - - - {BadgeSection} - - - - )} - {isMobile && ( - - document.getElementById('drawer-title-container-min') - } - > - - - {planter.first_name}{' '} - {planter.last_name && planter.last_name.slice(0, 1)}. - - - - )} - - {!isMobile && ( - - - {planter.first_name}{' '} - {planter.last_name && planter.last_name.slice(0, 1)}. - - - - Planter since - - - } - /> - - - - - - {BadgeSection} - - - )} - - - - Featured trees by {planter.first_name} - - `/planters/${planter.id}/trees/${item.id}`} - /> - - - - - setIsPlanterTab(true)} - iconURI={TreeIcon} - iconProps={{ - sx: { - '& path': { - fill: ({ palette }) => palette.primary.main, - }, - }, - }} - title="Trees Planted" - text={treeCount} - disabled={!isPlanterTab} - /> - - - setIsPlanterTab(false) - : undefined - } - iconURI={PeopleIcon} - iconProps={{ - sx: { - '& path': { - fill: ({ palette }) => palette.text.primary, - }, - }, - }} - title="Associated Orgs" - text={ - planter.associatedOrganizations.organizations.length || ( - - Individual planter - - ) - } - disabled={isPlanterTab} - /> - - - - {planter.continent_name && ( - - - - )} - - - Species of trees planted - - - {planter.species.species.map((species) => ( - - ))} - - {(!planter.species.species || - planter.species.species.length === 0) && ( - NO DATA YET - )} - - - {planter.associatedOrganizations.organizations.map((org) => ( - <> - - - - ))} - - - - About the Planter - - - - - - - - - {nextExtraIsEmbed && ( - document.getElementById('embed-logo-container')} - > - - - )} - - ); -} - -async function serverSideData(params) { - const id = params.planterid; - const planter = await getPlanterById(id); - const data = await getOrgLinks(planter.links); - return { - planter: { ...planter, ...data }, - }; -} - -const getStaticProps = wrapper(async ({ params }) => { - const props = await serverSideData(params); - return { - props, - revalidate: Number(process.env.NEXT_CACHE_REVALIDATION_OVERRIDE) || 30, - }; -}); - -// eslint-disable-next-line -const getStaticPaths = async () => { - return { - paths: [], - fallback: 'blocking', - }; -}; - -export { getStaticProps, getStaticPaths }; diff --git a/src/pages/tokens/[tokenid].js b/src/pages/tokens/[tokenid].js index df67a04c..228e74c7 100644 --- a/src/pages/tokens/[tokenid].js +++ b/src/pages/tokens/[tokenid].js @@ -39,7 +39,7 @@ import TreeIcon from 'images/icons/tree.svg'; import imagePlaceholder from 'images/image-placeholder.png'; import SearchIcon from 'images/search.svg'; import { useMapContext } from 'mapContext'; -import { getWalletById, getTokenById, getPlanterById } from 'models/api'; +import { getWalletById, getTokenById, getGrowerById } from 'models/api'; import { makeStyles } from 'models/makeStyles'; import * as pathResolver from 'models/pathResolver'; import { wrapper } from 'models/utils'; @@ -60,8 +60,7 @@ function handleShare() {} export default function Token(props) { log.warn('props:', props); - const { token, wallet, transactions, nextExtraIsEmbed, tree, planter } = - props; + const { token, wallet, transactions, nextExtraIsEmbed, tree, grower } = props; const theme = useTheme(); const { classes } = useStyles(); const mapContext = useMapContext(); @@ -535,10 +534,10 @@ export default function Token(props) { p: [2, 4], }} > - + @@ -697,14 +696,14 @@ async function serverSideData(params, query) { `${process.env.NEXT_PUBLIC_API}/transactions?token_id=${token.id}`, ); const { data: transactions } = res; - const planter = await getPlanterById(tree.planter_id); + const grower = await getGrowerById(tree.planter_id); result = { token, wallet, transactions, tree, - planter, + grower, }; } else { const token = await getTokenById(tokenid); @@ -722,14 +721,14 @@ async function serverSideData(params, query) { ); tree = res2.data; } - const planter = await getPlanterById(tree.planter_id); + const grower = await getGrowerById(tree.planter_id); result = { token, wallet, transactions, tree, - planter, + grower, }; } diff --git a/src/pages/trees/[treeid].js b/src/pages/trees/[treeid].js index 8a2617bb..d45cb2af 100644 --- a/src/pages/trees/[treeid].js +++ b/src/pages/trees/[treeid].js @@ -41,7 +41,6 @@ import { useMapContext } from 'mapContext'; import { getCaptures, getOrganizationById, - getPlanterById, getTreeById, getCapturesByTreeId, getGrowerById, @@ -235,8 +234,8 @@ export default function Tree({ ]} > {/* - - */} + + */} {isMobile && ( document.getElementById('drawer-title-container')} @@ -577,10 +576,10 @@ export default function Tree({ )} {/* */} + imageUrl={tree.image_url} + timeCreated={tree.time_created} + treeId={tree.id} + /> */} {organization && ( Date: Thu, 11 Apr 2024 16:41:29 -0700 Subject: [PATCH 04/11] feat: add fixture for grower image --- cypress/fixtures/images/grower.png | Bin 0 -> 68378 bytes .../tests/integration/growers/[growerid].cy.js | 7 ++++++- 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 cypress/fixtures/images/grower.png diff --git a/cypress/fixtures/images/grower.png b/cypress/fixtures/images/grower.png new file mode 100644 index 0000000000000000000000000000000000000000..337d19aa5ca69161e99f89f0278e954c0e92e3ed GIT binary patch literal 68378 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
F}ob~SqRAD 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

IX@X+84ZGCs@Jz`Z=`k+lUM{Y=Uu;Zi)1d$ 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 Z&OdDQ14CzLPv`&u002ovPDHLkV1kC3iSz&f literal 0 HcmV?d00001 diff --git a/cypress/tests/integration/growers/[growerid].cy.js b/cypress/tests/integration/growers/[growerid].cy.js index 8e3d3a70..82c6eb29 100644 --- a/cypress/tests/integration/growers/[growerid].cy.js +++ b/cypress/tests/integration/growers/[growerid].cy.js @@ -8,7 +8,12 @@ beforeEach(() => { describe('Grower Page', () => { it('renders with grower data', () => { const path = `/growers/${grower.id}`; - prepareNocks({ grower }); + const imageFixturePath = `images/grower.png`; + cy.fixture(imageFixturePath).then((image) => { + const blob = Cypress.Blob.base64StringToBlob(image, 'image/png'); + const image_url = Cypress.Blob.createObjectURL(blob); + prepareNocks({ grower: { ...grower, image_url } }); + }); cy.visit(path, { failOnStatusCode: false, From 505e8e75584928a68151ce7d98d28adb7d0236b2 Mon Sep 17 00:00:00 2001 From: Sam Rice Date: Thu, 18 Apr 2024 22:47:01 -0700 Subject: [PATCH 05/11] feat: update grower page according to v2 grower_accounts schema --- doc/examples/growers/100.json | 26 ++++- src/models/utils.js | 4 +- src/pages/growers/[growerid].js | 188 ++++++++++++++------------------ 3 files changed, 102 insertions(+), 116 deletions(-) diff --git a/doc/examples/growers/100.json b/doc/examples/growers/100.json index 5782fc12..5ab30d99 100644 --- a/doc/examples/growers/100.json +++ b/doc/examples/growers/100.json @@ -1,16 +1,30 @@ { "id": 100, + "wallet": "Stan.Kub68", + "person_id": 101, + "organization_id": 1, "first_name": "Stanley", + "email": "stanley@mail.com", + "phone": "555-555-5555", + "image_url": "https://treetracker-dev-images.s3.eu-central-1.amazonaws.com/2020.10.19.09.47.53_-5.508107173727935_38.981361706266256_39f0cc9d-0f13-4547-8142-150f15cabb67_IMG_20201019_094513_6614320100195503436.jpg", + "image_rotation": 0, + "status": "active", + "first_registration_at": "2024-04-17 12:00:00+00", + "created_at": "2024-04-17 12:01:00+00", + "updated_at": "2024-04-17 21:00:00+00", "last_name": "Kubrick", + "location": "POINT(-6.369028 34.888821)", + "lon": "-6.369028", + "lat": "34.888821", + "bulk_pack_file_name": "foo", + "gender": "male", + "about": "Stanley Kubrick is a visionary filmmaker known for his meticulous attention to detail, innovative storytelling, and exploration of profound philosophical and psychological themes across a diverse range of genres. He also tends to several trees.", + "reference_id": 11, + "show_in_map": false, "links": { "featured_trees": "/trees?grower_id=100&limit=4", "associated_organizations": "/organizations?grower_id=100", "species": "/species?grower_id=100" }, - "image_url": "https://treetracker-dev-images.s3.eu-central-1.amazonaws.com/2020.10.19.09.47.53_-5.508107173727935_38.981361706266256_39f0cc9d-0f13-4547-8142-150f15cabb67_IMG_20201019_094513_6614320100195503436.jpg", - "trees_planted": 4, - "about": "Greenway is a Youth-Driven Environmental Protection Organization providing alternative solutions to single-use plastic and planting carbon-sucking trees for socio-economic development and reducing climate crisis. Our social work includes reforestation, landscape restoration, climate education, awareness campaign, conducting research, outreach activities, and collaborating with key stakeholders to implement sustainable solutions.", - "mission": "To combat climate change, desertification, land degradation, carbon emission by inspiring healthier communities affected by severe climate disorder and modestly reducing pollution by 2050.", - "created_time": "2018-01-01", - "country": "Tanzania" + "continent_name": "Asia" } \ No newline at end of file diff --git a/src/models/utils.js b/src/models/utils.js index 7b77bdca..49e9e2c0 100644 --- a/src/models/utils.js +++ b/src/models/utils.js @@ -199,7 +199,7 @@ const optimizeThemeFonts = (theme) => { }; function getGrowerName(firstName, lastName) { - return `${firstName} ${(lastName && lastName.slice(0, 1)) || ''}`; + return `${firstName} ${lastName ? `${lastName.slice(0, 1)}.` : ''}`; } /** @@ -240,6 +240,8 @@ const wrapper = (callback) => (params) => }); const getLocationString = (country, continent) => { + // TODO: Replace this with new implementation to use parameter of Postgres type geometry(Point,4326) + if (!country && !continent) return 'Unknown'; if (!country) return continent; if (!continent) return country; diff --git a/src/pages/growers/[growerid].js b/src/pages/growers/[growerid].js index 6e962385..d7372e47 100644 --- a/src/pages/growers/[growerid].js +++ b/src/pages/growers/[growerid].js @@ -6,8 +6,6 @@ import Divider from '@mui/material/Divider'; import Grid from '@mui/material/Grid'; import Portal from '@mui/material/Portal'; import Typography from '@mui/material/Typography'; -import log from 'loglevel'; -import { marked } from 'marked'; import moment from 'moment'; import { useRouter } from 'next/router'; import { useEffect, useMemo, useState } from 'react'; @@ -67,21 +65,20 @@ const useStyles = makeStyles()((theme) => ({ })); export default function Grower(props) { - log.warn('props for grower page:', props); const { grower, nextExtraIsEmbed } = props; - const { featuredTrees } = grower; - const treeCount = featuredTrees?.total; const mapContext = useMapContext(); + const { classes } = useStyles(); const isMobile = useMobile(); - const router = useRouter(); - const [isGrowerTab, setIsGrowerTab] = useState(true); - const { setTitlesData } = useDrawerContext(); - const { classes } = useStyles(); + const treeCount = grower.featuredTrees?.trees.length; + const formattedGrowerName = getGrowerName( + grower.first_name, + grower.last_name, + ); // try to find first tree image or default image return const backgroundPic = @@ -92,22 +89,20 @@ export default function Grower(props) { setTitlesData({ firstName: grower.first_name, lastName: grower.last_name, - createdTime: grower.created_time, + createdTime: grower.created_at, }); - }, [grower.created_time, grower.first_name, grower.last_name, setTitlesData]); + }, [grower.created_at, grower.first_name, grower.last_name, setTitlesData]); useEffect(() => { async function reload() { // manipulate the map const { map } = mapContext; if (map && grower) { - // map.flyTo(tree.lat, tree.lon, 16); await map.setFilters({ userid: grower.id, }); const bounds = pathResolver.getBounds(router); if (bounds) { - log.warn('goto bounds found in url'); await map.gotoBounds(bounds); } else { const view = await map.getInitialView(); @@ -116,7 +111,7 @@ export default function Grower(props) { } } reload(); - }, [mapContext, grower]); + }, [mapContext, grower, router]); const BadgeSection = useMemo( () => ( @@ -134,9 +129,7 @@ export default function Grower(props) { return ( <> - + - {/* - - */} - {/* - - */} {!isMobile && ( , name: 'Home', url: '/', }, { icon: grower.image_url, - name: `${getGrowerName(grower.first_name, grower.last_name)}`, + name: formattedGrowerName, }, ]} /> @@ -211,74 +197,9 @@ export default function Grower(props) { rotation={grower.image_rotation} /> - {isMobile && ( - document.getElementById('drawer-title-container')} - > - - - {getGrowerName(grower.first_name, grower.last_name)} - - - - Grower since - - - } - /> - - - - - - {BadgeSection} - - - - )} - {isMobile && ( - - document.getElementById('drawer-title-container-min') - } - > - - - {grower.first_name}{' '} - {grower.last_name && grower.last_name.slice(0, 1)}. - - - - )} {!isMobile && ( - - {grower.first_name}{' '} - {grower.last_name && grower.last_name.slice(0, 1)}. - + {formattedGrowerName} )} + {isMobile && ( + <> + + document.getElementById('drawer-title-container') + } + > + + {formattedGrowerName} + + + Grower since + + + } + /> + + + + + + {BadgeSection} + + + + + document.getElementById('drawer-title-container-min') + } + > + + {formattedGrowerName} + + + + )} `/growers/${grower.id}/trees/${item.id}`} /> @@ -389,14 +363,14 @@ export default function Grower(props) { display: isGrowerTab ? 'block' : 'none', }} > - {grower.continent_name && ( - - - - )} + +

Sam
+ refactor + /> + - + {grower.about || 'NO DATA YET'} Date: Thu, 18 Apr 2024 22:52:15 -0700 Subject: [PATCH 06/11] fix: remove deprecated field on grower mock data --- doc/examples/growers/100.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/doc/examples/growers/100.json b/doc/examples/growers/100.json index 5ab30d99..299a4628 100644 --- a/doc/examples/growers/100.json +++ b/doc/examples/growers/100.json @@ -25,6 +25,5 @@ "featured_trees": "/trees?grower_id=100&limit=4", "associated_organizations": "/organizations?grower_id=100", "species": "/species?grower_id=100" - }, - "continent_name": "Asia" + } } \ No newline at end of file From f28643bacbce4c52c0cd4a6c88cb8d1a872c6cf4 Mon Sep 17 00:00:00 2001 From: Sam Rice Date: Fri, 19 Apr 2024 00:12:51 -0700 Subject: [PATCH 07/11] fix: remove planter references from TreeInfoDialog and capture page --- .../integration/growers/[growerid].cy.js | 3 +- cypress/tests/integration/nockRoutes.js | 6 -- cypress/tests/integration/top.cy.js | 4 +- src/components/TreeInfoDialog.cy.js | 8 +- src/components/TreeInfoDialog.js | 34 ++++---- src/pages/trees/[treeid].js | 78 +++++++++---------- src/pages/v2/captures/[captureid].js | 2 +- 7 files changed, 63 insertions(+), 72 deletions(-) diff --git a/cypress/tests/integration/growers/[growerid].cy.js b/cypress/tests/integration/growers/[growerid].cy.js index 82c6eb29..4b54a87e 100644 --- a/cypress/tests/integration/growers/[growerid].cy.js +++ b/cypress/tests/integration/growers/[growerid].cy.js @@ -8,8 +8,7 @@ beforeEach(() => { describe('Grower Page', () => { it('renders with grower data', () => { const path = `/growers/${grower.id}`; - const imageFixturePath = `images/grower.png`; - cy.fixture(imageFixturePath).then((image) => { + cy.fixture('images/grower.png').then((image) => { const blob = Cypress.Blob.base64StringToBlob(image, 'image/png'); const image_url = Cypress.Blob.createObjectURL(blob); prepareNocks({ grower: { ...grower, image_url } }); diff --git a/cypress/tests/integration/nockRoutes.js b/cypress/tests/integration/nockRoutes.js index 86c9b38a..56ddfcb4 100644 --- a/cypress/tests/integration/nockRoutes.js +++ b/cypress/tests/integration/nockRoutes.js @@ -38,12 +38,6 @@ export function getNockRoutes( statusCode: 200, body: tree, }, - { - method: 'GET', - path: `/growers/${grower.grower_id}`, - statusCode: 200, - body: grower, - }, { method: 'GET', path: `/organizations/${organization.id}`, diff --git a/cypress/tests/integration/top.cy.js b/cypress/tests/integration/top.cy.js index 12d087ed..457d06bb 100644 --- a/cypress/tests/integration/top.cy.js +++ b/cypress/tests/integration/top.cy.js @@ -1,7 +1,7 @@ import { prepareNocks, clearNocks } from './nockRoutes'; import leaders from '../../../doc/examples/countries/leader.json'; +import grower100 from '../../../doc/examples/growers/100.json'; import organization1 from '../../../doc/examples/organizations/1.json'; -import planter940 from '../../../doc/examples/planters/940.json'; import wallets from '../../../doc/examples/wallets/180Earth.json'; import capture from '../../fixtures/capture.json'; import tree186734 from '../../fixtures/tree186734.json'; @@ -39,7 +39,7 @@ describe('top', () => { path: '/query/v2/growers/featured', statusCode: 200, body: { - grower_accounts: [planter940], + grower_accounts: [grower100], }, }); diff --git a/src/components/TreeInfoDialog.cy.js b/src/components/TreeInfoDialog.cy.js index 4dcc05ee..0e26f472 100644 --- a/src/components/TreeInfoDialog.cy.js +++ b/src/components/TreeInfoDialog.cy.js @@ -1,19 +1,19 @@ +import { mountWithTheme as mount } from 'models/test-utils'; import TreeInfoDialog from './TreeInfoDialog'; +import grower from '../../doc/examples/growers/100.json'; import organization from '../../doc/examples/organizations/1.json'; -import planter from '../../doc/examples/planters/940.json'; import tree from '../../doc/examples/trees/186734.json'; -import { mountWithTheme as mount } from '../models/test-utils'; describe('TreeInfoDialog', () => { it('renders without organization', () => { - mount(); + mount(); }); it('renders withorganization', () => { mount( , ); diff --git a/src/components/TreeInfoDialog.js b/src/components/TreeInfoDialog.js index 162e12ed..4c85742c 100644 --- a/src/components/TreeInfoDialog.js +++ b/src/components/TreeInfoDialog.js @@ -1,3 +1,4 @@ +/* eslint-disable @next/next/no-img-element */ // /* eslint-disable @next/next/no-Image-element */ import CloseIcon from '@mui/icons-material/Close'; import { @@ -18,15 +19,15 @@ import { DialogTitle, } from '@mui/material'; import { useState } from 'react'; +import { useFullscreen, useMobile } from 'hooks/globalHooks'; +import HeartIcon from 'images/icons/heart.svg'; +import ShareIcon from 'images/icons/share-icon.svg'; +import imagePlaceholder from 'images/image-placeholder.png'; +import MaxIcon from 'images/max.svg'; +import { makeStyles } from 'models/makeStyles'; +import { getGrowerName } from 'models/utils'; import Share from './Share'; import Icon from './common/CustomIcon'; -import { useFullscreen, useMobile } from '../hooks/globalHooks'; -import HeartIcon from '../images/icons/heart.svg'; -import ShareIcon from '../images/icons/share-icon.svg'; -import imagePlaceholder from '../images/image-placeholder.png'; -import MaxIcon from '../images/max.svg'; -import { makeStyles } from '../models/makeStyles'; -import * as utils from '../models/utils'; const useStyles = makeStyles()(() => ({ imageLarge: { @@ -116,7 +117,7 @@ function CustomImageItem(props) { } export default function TreeInfoDialog(props) { - const { tree, planter, organization } = props; + const { tree, grower, organization } = props; const { classes } = useStyles(); const isMobile = useMobile(); @@ -273,17 +274,14 @@ export default function TreeInfoDialog(props) { @@ -295,7 +293,7 @@ export default function TreeInfoDialog(props) { alt={organization.name} /> diff --git a/src/pages/trees/[treeid].js b/src/pages/trees/[treeid].js index d45cb2af..0439d4a7 100644 --- a/src/pages/trees/[treeid].js +++ b/src/pages/trees/[treeid].js @@ -46,12 +46,12 @@ import { getGrowerById, } from 'models/api'; import * as pathResolver from 'models/pathResolver'; -import * as utils from 'models/utils'; +import { wrapper, getGrowerName } from 'models/utils'; export default function Tree({ tree, captures, - planter, + grower, organization, nextExtraIsEmbed, nextExtraKeyword, @@ -70,7 +70,7 @@ export default function Tree({ }); const isMobile = useMobile(); const isEmbed = useEmbed(); - const isPlanterContext = context && context.name === 'planters'; + const isGrowerContext = context && context.name === 'growers'; const isOrganizationContext = context && context.name === 'organizations'; const { setTitlesData } = useDrawerContext(); @@ -136,8 +136,8 @@ export default function Tree({ log.warn('map ,tree, context in tree page:', map, tree, context); if (map && tree?.lat && tree?.lon) { if (context && context.name) { - if (isPlanterContext) { - log.warn('set planter filter', context.id); + if (isGrowerContext) { + log.warn('set grower filter', context.id); await map.setFilters({ userid: context.id, }); @@ -188,8 +188,6 @@ export default function Tree({ reload(); }, [map, tree.lat, tree.lon]); - log.warn(planter, 'planter'); - // storing under variable with useMemo wrapped // to reuse the same component for mobile and desktop and // avoid re-rendering of badge components @@ -234,8 +232,8 @@ export default function Tree({ ]} > {/* - - */} + + */} {isMobile && ( document.getElementById('drawer-title-container')} @@ -357,14 +355,14 @@ export default function Tree({ name: 'Home', url: '/', }, - ...(isPlanterContext + ...(isGrowerContext ? [ { - url: `/planters/${planter.id}`, - icon: planter.image_url, - name: `${utils.getPlanterName( - planter.first_name, - planter.last_name, + url: `/growers/${grower.id}`, + icon: grower.image_url, + name: `${getGrowerName( + grower.first_name, + grower.last_name, )}`, }, ] @@ -472,7 +470,7 @@ export default function Tree({ /> @@ -576,10 +574,10 @@ export default function Tree({ )} {/* */} + imageUrl={tree.image_url} + timeCreated={tree.time_created} + treeId={tree.id} +/> */} {organization && ( @@ -767,7 +765,7 @@ export default function Tree({ height: '120px', margin: '10px', }} - src={isPlanterContext ? planter.image_url : organization.logo_url} + src={isGrowerContext ? grower.image_url : organization.logo_url} variant="rounded" /> @@ -780,31 +778,33 @@ export default function Tree({ async function serverSideData(params) { const { treeid } = params; const tree = await getTreeById(treeid); - const { planter_id, planting_organization_id } = tree; - const planter = await getGrowerById(planter_id); + const { + planter_id: grower_id, + planting_organization_id: growing_organization_id, + } = tree; + const grower = await getGrowerById(grower_id); const captures = await getCaptures(); // const captures = await getTreeCaptures(treeid) // comment in when captures in v2 database have valid tree_id's. let organization = null; - if (planting_organization_id) { - log.warn('load org from planting_orgniazation_id'); - organization = await getOrganizationById(planting_organization_id); - } else if (planter.organization_id) { - log.warn('load org from planter. organization_id'); - organization = await getOrganizationById(planter.organization_id); + if (growing_organization_id) { + organization = await getOrganizationById(growing_organization_id); + } else if (grower.organization_id) { + log.warn('load org from grower. organization_id'); + organization = await getOrganizationById(grower.organization_id); } else { - log.warn('can not load org for tree:', tree, planter); + log.warn('can not load org for tree:', tree, grower); } return { tree, captures, - planter, + grower, organization, }; } -const getStaticProps = utils.wrapper(async ({ params }) => { +const getStaticProps = wrapper(async ({ params }) => { const props = await serverSideData(params); return { props, diff --git a/src/pages/v2/captures/[captureid].js b/src/pages/v2/captures/[captureid].js index 828e1e2c..75060bb7 100644 --- a/src/pages/v2/captures/[captureid].js +++ b/src/pages/v2/captures/[captureid].js @@ -466,7 +466,7 @@ export default function Capture({ /> From b20b1219fe9daf561d203350928430c63e0c4190 Mon Sep 17 00:00:00 2001 From: Sam Rice Date: Sat, 20 Apr 2024 17:16:27 -0700 Subject: [PATCH 08/11] fix: corrected data type in grower account fixture --- cypress/fixtures/tree186734.json | 2 +- doc/examples/growers/100.json | 4 ++-- doc/examples/organizations/1.json | 2 +- src/models/apiPaths.js | 1 - 4 files changed, 4 insertions(+), 5 deletions(-) diff --git a/cypress/fixtures/tree186734.json b/cypress/fixtures/tree186734.json index 84a284b6..f4110776 100644 --- a/cypress/fixtures/tree186734.json +++ b/cypress/fixtures/tree186734.json @@ -13,7 +13,7 @@ "status":"active", "attributes":null, "species_id":null, - "planter_id":940, + "grower_id":940, "name":"The Best Tree" } diff --git a/doc/examples/growers/100.json b/doc/examples/growers/100.json index 299a4628..be5d9f6e 100644 --- a/doc/examples/growers/100.json +++ b/doc/examples/growers/100.json @@ -14,8 +14,8 @@ "updated_at": "2024-04-17 21:00:00+00", "last_name": "Kubrick", "location": "POINT(-6.369028 34.888821)", - "lon": "-6.369028", - "lat": "34.888821", + "lon": -6.369028, + "lat": 34.888821, "bulk_pack_file_name": "foo", "gender": "male", "about": "Stanley Kubrick is a visionary filmmaker known for his meticulous attention to detail, innovative storytelling, and exploration of profound philosophical and psychological themes across a diverse range of genres. He also tends to several trees.", diff --git a/doc/examples/organizations/1.json b/doc/examples/organizations/1.json index fa1564b7..3e500a88 100644 --- a/doc/examples/organizations/1.json +++ b/doc/examples/organizations/1.json @@ -10,7 +10,7 @@ "map_name":"freetown", "links": { "featured_trees": "/trees?organization_id=1&limit=4", - "associated_planters": "/planters?organization_id=1&limit=4", + "associated_growers": "/growers?organization_id=1&limit=4", "species": "/species?organization_id=1" } } diff --git a/src/models/apiPaths.js b/src/models/apiPaths.js index 6232e34a..dc17996c 100644 --- a/src/models/apiPaths.js +++ b/src/models/apiPaths.js @@ -15,7 +15,6 @@ const apiPaths = { urlJoin(host, `/captures?tree_id=${treeid}`), captures: (id = '') => urlJoin(host, `captures/${id}`), growers: (id = '') => urlJoin(host, `growers/${id}`), - planters: (id = '') => urlJoin(host, `planters/${id}`), stakeHolders: (id = '') => urlJoin(hostStakeholder, `/stakeholders/${id}`), species: urlJoin(host, '/species'), organization: (id = '') => urlJoin(host, `/organizations/${id}`), From 0eb79a69b24604ebad53afd83861eb30062f6585 Mon Sep 17 00:00:00 2001 From: Sam Rice Date: Sun, 21 Apr 2024 22:52:58 -0700 Subject: [PATCH 09/11] fix: revert to previous set of Cypress intercepts to fix failing tests --- cypress/tests/integration/nockRoutes.js | 48 ++++++++++++++++--------- cypress/tests/integration/readme.md | 2 +- doc/examples/organizations/1.json | 2 +- doc/examples/planters/940.json | 16 +++++++++ next.config.js | 8 ++--- src/models/pathResolver.js | 8 ++--- src/pages/index.js | 4 +-- src/pages/trees/[treeid].js | 13 +++---- 8 files changed, 65 insertions(+), 36 deletions(-) create mode 100644 doc/examples/planters/940.json diff --git a/cypress/tests/integration/nockRoutes.js b/cypress/tests/integration/nockRoutes.js index 56ddfcb4..58c5a637 100644 --- a/cypress/tests/integration/nockRoutes.js +++ b/cypress/tests/integration/nockRoutes.js @@ -1,5 +1,6 @@ import grower100 from '../../../doc/examples/growers/100.json'; import organization1 from '../../../doc/examples/organizations/1.json'; +import planter940 from '../../../doc/examples/planters/940.json'; import { defaultConfig } from '../../../src/context/configContext'; import capture1 from '../../fixtures/capture.json'; import leader from '../../fixtures/countries/leader.json'; @@ -10,13 +11,16 @@ export function getNockRoutes( tree: {}, organization: {}, grower: {}, + planter: {}, capture: {}, }, ) { const organization = { ...organization1, ...props.organization }; const grower = { ...grower100, ...props.grower }; + const planter = { ...planter940, ...props.planter }; const tree = { ...tree186734, ...props.tree }; const capture = { ...capture1, ...props.capture }; + return [ { method: 'GET', @@ -26,45 +30,57 @@ export function getNockRoutes( }, { method: 'GET', - path: '/trees/featured', + path: grower.links.species, statusCode: 200, body: { - trees: [tree], + species: [tree], }, }, { method: 'GET', - path: `/trees/${tree.id}`, + path: grower.links.associated_organizations, statusCode: 200, - body: tree, + body: { organizations: [organization] }, }, { method: 'GET', - path: `/organizations/${organization.id}`, + path: grower.links.featured_trees, statusCode: 200, - body: organization, + body: { + trees: [tree], + }, }, { method: 'GET', - path: grower.links.species, + path: `/planters/${planter.id}`, + statusCode: 200, + body: planter, + }, + { + method: 'GET', + path: '/trees/featured', statusCode: 200, body: { - species: [tree], + trees: [tree], }, }, { method: 'GET', - path: grower.links.associated_organizations, + path: `/trees/${tree.id}`, statusCode: 200, - body: { organizations: [organization] }, + body: tree, }, { method: 'GET', - path: grower.links.featured_trees, + path: `/growers/${planter.id}`, statusCode: 200, - body: { - trees: [tree], - }, + body: planter, + }, + { + method: 'GET', + path: `/organizations/${organization.id}`, + statusCode: 200, + body: organization, }, { method: 'GET', @@ -76,9 +92,9 @@ export function getNockRoutes( }, { method: 'GET', - path: organization.links.associated_growers, + path: organization.links.associated_planters, statusCode: 200, - body: { growers: [grower] }, + body: { planters: [planter] }, }, { method: 'GET', diff --git a/cypress/tests/integration/readme.md b/cypress/tests/integration/readme.md index cd38f362..349514ac 100644 --- a/cypress/tests/integration/readme.md +++ b/cypress/tests/integration/readme.md @@ -110,7 +110,7 @@ path: '/query/trees/951836', statusCode: 200, body: { id:951836, -planterid: 940 +grower_id: 940 }, }); ``` diff --git a/doc/examples/organizations/1.json b/doc/examples/organizations/1.json index 3e500a88..fa1564b7 100644 --- a/doc/examples/organizations/1.json +++ b/doc/examples/organizations/1.json @@ -10,7 +10,7 @@ "map_name":"freetown", "links": { "featured_trees": "/trees?organization_id=1&limit=4", - "associated_growers": "/growers?organization_id=1&limit=4", + "associated_planters": "/planters?organization_id=1&limit=4", "species": "/species?organization_id=1" } } diff --git a/doc/examples/planters/940.json b/doc/examples/planters/940.json new file mode 100644 index 00000000..dcaf07b8 --- /dev/null +++ b/doc/examples/planters/940.json @@ -0,0 +1,16 @@ +{ + "id": 940, + "first_name": "Sebastian ", + "last_name": "Gaertner", + "image_url": "https://treetracker-dev-images.s3.eu-central-1.amazonaws.com/2020.10.19.09.47.53_-5.508107173727935_38.981361706266256_39f0cc9d-0f13-4547-8142-150f15cabb67_IMG_20201019_094513_6614320100195503436.jpg", + "trees_planted": 4, + "about": "Greenway is a Youth-Driven Environmental Protection Organization providing alternative solutions to single-use plastic and planting carbon-sucking trees for socio-economic development and reducing climate crisis. Our social work includes reforestation, landscape restoration, climate education, awareness campaign, conducting research, outreach activities, and collaborating with key stakeholders to implement sustainable solutions.", + "mission": "To combat climate change, desertification, land degradation, carbon emission by inspiring healthier communities affected by severe climate disorder and modestly reducing pollution by 2050.", + "created_time": "2018-01-01", + "country": "Tanzania", + "links": { + "featured_trees": "/trees?planter_id=940&limit=4", + "associated_organizations": "/organizations?planter_id=940", + "species": "/species?planter_id=940" + } +} \ No newline at end of file diff --git a/next.config.js b/next.config.js index 2407c2c3..04d89040 100644 --- a/next.config.js +++ b/next.config.js @@ -17,7 +17,7 @@ module.exports = { async rewrites() { return [ { - source: '/planters/:planterId(\\d{1,})/trees/:treeId(\\d{1,})', + source: '/growers/:growerId(\\d{1,})/trees/:treeId(\\d{1,})', destination: '/trees/:treeId(\\d{1,})', }, { @@ -54,15 +54,15 @@ module.exports = { permanent: true, }, { - source: '/:path((?!planters).*)', + source: '/:path((?!growers).*)', has: [ { type: 'query', key: 'userid', - value: '(?(\\d{1,}))', + value: '(?(\\d{1,}))', }, ], - destination: '/planters/:planterId(\\d{1,})', + destination: '/growers/:growerId(\\d{1,})', permanent: true, }, { diff --git a/src/models/pathResolver.js b/src/models/pathResolver.js index 64d2338d..a90ee120 100644 --- a/src/models/pathResolver.js +++ b/src/models/pathResolver.js @@ -2,13 +2,13 @@ import log from 'loglevel'; import * as utils from './utils'; const MAP_URL_PATTERN = - /^(\/(planters|organizations|wallets)\/([a-z0-9-]+))?(\/(trees|tokens)\/([a-z0-9-]+))?(\?.*)?$/; + /^(\/(growers|organizations|wallets)\/([a-z0-9-]+))?(\/(trees|tokens)\/([a-z0-9-]+))?(\?.*)?$/; // v2 api pattern const MAP_URL_PATTERNV2 = - /^(\/v2\/(planters|organizations|wallets)\/([a-z0-9-]+))?(\/v2\/(trees|tokens|captures)\/([a-z0-9-]+))?(\?.*)?$/; + /^(\/v2\/(growers|organizations|wallets)\/([a-z0-9-]+))?(\/v2\/(trees|tokens|captures)\/([a-z0-9-]+))?(\?.*)?$/; -// 1: (/planters/1234) -// 2: (planters) +// 1: (/growers/1234) +// 2: (growers) // 3: (1234) // 4: (/tokens/1234) // 5: (tokens) diff --git a/src/pages/index.js b/src/pages/index.js index 0f1ea949..e3152d79 100644 --- a/src/pages/index.js +++ b/src/pages/index.js @@ -1,6 +1,6 @@ import log from 'loglevel'; import Head from 'next/head'; -import Home from '../components/Home'; +import Home from 'components/Home'; export default function Homepage({ nextExtraIsEmbed }) { log.warn(nextExtraIsEmbed); @@ -9,7 +9,7 @@ export default function Homepage({ nextExtraIsEmbed }) { {/* - + */} {isMobile && ( */} {organization && ( Date: Tue, 30 Apr 2024 20:17:38 -0700 Subject: [PATCH 10/11] fix: package version number --- package-lock.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 6d9ea7bf..07d05357 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "client", - "version": "2.6.0-v2.9", + "version": "2.6.0-v2.5", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "client", - "version": "2.6.0-v2.9", + "version": "2.6.0-v2.5", "dependencies": { "@emotion/cache": "^11.5.0", "@emotion/react": "^11.5.0", From f16cde9d7ef62f0833bc3f8062d7aefc6c975b44 Mon Sep 17 00:00:00 2001 From: Sam Rice Date: Wed, 1 May 2024 12:19:53 -0700 Subject: [PATCH 11/11] feat: add additional assertions to grower page test --- cypress/fixtures/capture.json | 58 ++-- cypress/fixtures/oldTree.json | 121 ++++--- cypress/fixtures/tree186734.json | 37 +-- .../integration/growers/[growerid].cy.js | 53 +++- cypress/tests/integration/moneytree.cy.js | 4 +- doc/examples/growers/100.json | 2 +- src/pages/growers/[growerid].js | 8 +- src/pages/top.js | 297 +++++++++--------- 8 files changed, 310 insertions(+), 270 deletions(-) diff --git a/cypress/fixtures/capture.json b/cypress/fixtures/capture.json index bc902cfc..3ce69f87 100644 --- a/cypress/fixtures/capture.json +++ b/cypress/fixtures/capture.json @@ -1,27 +1,33 @@ { - "id":"0c2d6e3a-d02a-4edf-bcdc-893e1f7a4817", - "reference_id":"999940", - "tree_id":"da4d3ed8-8655-44c5-a7ba-d4c45a0dfb10", - "image_url":"https://treetracker-production-images.s3.eu-central-1.amazonaws.com/2023.02.17.11.59.47_38.364922414000006_-122.51189396800001_a906ab31-5169-421c-8df3-be43f24d4d9c_IMG_20230217_111351_7976264471450881710.jpg","lat":"38.364922414000000","lon":"-122.51189396800000", - "estimated_geometric_location":"0101000020E610000019E5EADEC2A05EC08BDA14C7B52E4340", - "gps_accuracy":null, - "morphology":null, - "age":0,"note":"", - "attributes":null, - "domain_specific_data":null, - "created_at":"2023-07-31T02:24:26.446Z", - "updated_at":"2023-07-31T02:24:26.446Z", - "estimated_geographic_location":"0101000020E610000019E5EADEC2A05EC08BDA14C7B52E4340", - "device_configuration_id":"23d77b5b-2b87-42c7-93c3-8eb837541052", - "session_id":"39ab7585-6360-4d6a-9efa-52f0e35c4eaf", - "status":"active", - "grower_account_id":"c82cc807-8e35-418b-88ce-fda398cf4e89", - "planting_organization_id":null,"species_id":null, - "captured_at":"2023-02-17T19:15:19.000Z","token_id":null,"token_issued":null, - "tags":null, - "device_identifier":"something", - "wallet":"wallet10@test", - "grower_reference_id":null, - "wallet_name":null, - "wallet_token_id":null -} \ No newline at end of file + "id": "0c2d6e3a-d02a-4edf-bcdc-893e1f7a4817", + "reference_id": "999940", + "tree_id": "da4d3ed8-8655-44c5-a7ba-d4c45a0dfb10", + "image_url": "https://treetracker-production-images.s3.eu-central-1.amazonaws.com/2023.02.17.11.59.47_38.364922414000006_-122.51189396800001_a906ab31-5169-421c-8df3-be43f24d4d9c_IMG_20230217_111351_7976264471450881710.jpg", + "lat": "38.364922414000000", + "lon": "-122.51189396800000", + "estimated_geometric_location": "0101000020E610000019E5EADEC2A05EC08BDA14C7B52E4340", + "gps_accuracy": null, + "morphology": null, + "age": 0, + "note": "", + "attributes": null, + "domain_specific_data": null, + "created_at": "2023-07-31T02:24:26.446Z", + "updated_at": "2023-07-31T02:24:26.446Z", + "estimated_geographic_location": "0101000020E610000019E5EADEC2A05EC08BDA14C7B52E4340", + "device_configuration_id": "23d77b5b-2b87-42c7-93c3-8eb837541052", + "session_id": "39ab7585-6360-4d6a-9efa-52f0e35c4eaf", + "status": "active", + "grower_account_id": "c82cc807-8e35-418b-88ce-fda398cf4e89", + "planting_organization_id": null, + "species_id": null, + "captured_at": "2023-02-17T19:15:19.000Z", + "token_id": null, + "token_issued": null, + "tags": null, + "device_identifier": "something", + "wallet": "wallet10@test", + "grower_reference_id": null, + "wallet_name": null, + "wallet_token_id": null +} diff --git a/cypress/fixtures/oldTree.json b/cypress/fixtures/oldTree.json index 45a22dd8..a6b4e17a 100644 --- a/cypress/fixtures/oldTree.json +++ b/cypress/fixtures/oldTree.json @@ -1,63 +1,62 @@ { - "id": 186734, - "time_created": "2020-10-19T06:46:40.000Z", - "time_updated": "2020-10-19T06:46:40.000Z", - "missing": false, - "priority": false, - "cause_of_death_id": null, - "planter_id": 940, - "primary_location_id": null, - "settings_id": null, - "override_settings_id": null, - "dead": 0, - "photo_id": null, - "image_url": "https://treetracker-dev-images.s3.eu-central-1.amazonaws.com/2020.10.19.09.50.38_-5.508172399749922_38.98146973686408_6bebe71e-5369-4ae0-8c47-9eeff6599fb0_IMG_20201019_094615_7537040365910944885.jpg", - "certificate_id": null, - "estimated_geometric_location": "0101000020E610000027ECE2CCA07D43407E9F76585E0816C0", - "lat": "-5.508172399749922", - "lon": "38.98146973686408", - "gps_accuracy": 4, - "active": true, - "planter_photo_url": "https://treetracker-dev-images.s3.eu-central-1.amazonaws.com/2020.10.19.09.47.53_-5.508107173727935_38.981361706266256_39f0cc9d-0f13-4547-8142-150f15cabb67_IMG_20201019_094513_6614320100195503436.jpg", - "device_id": null, - "sequence": null, - "note": "", - "verified": false, - "uuid": "38572a5b-ac5b-4a77-943a-72254376735e", - "approved": false, - "status": "planted", - "cluster_regions_assigned": true, - "species_id": null, - "planting_organization_id": 1, - "payment_id": null, - "contract_id": null, - "token_issued": false, - "morphology": null, - "age": null, - "species": null, - "capture_approval_tag": null, - "rejection_reason": null, - "matching_hash": null, - "device_identifier": "651f04008af0d91a", - "images": {}, - "domain_specific_data": {}, - "token_id": null, - "name": null, - "earnings_id": null, - "species_name": null, - "first_name": "Sebastian ", - "last_name": "Gaertner", - "user_image_url": "https://treetracker-production.nyc3.digitaloceanspaces.com/2019.07.10.18.32.42_b4fad89a-10b6-40cc-a134-0085d0e581d2_IMG_20190710_183201_8089920786231467340.jpg", - "token_uuid": null, - "wallet": null, - "attributes": { - "dbh": "13", - "abs_step_count": "75351", - "delta_step_count": "0", - "height_color": "yellow", - "app_build": "1.4.0", - "app_flavor": "justdiggit", - "app_version": "113" - } + "id": 186734, + "time_created": "2020-10-19T06:46:40.000Z", + "time_updated": "2020-10-19T06:46:40.000Z", + "missing": false, + "priority": false, + "cause_of_death_id": null, + "planter_id": 940, + "primary_location_id": null, + "settings_id": null, + "override_settings_id": null, + "dead": 0, + "photo_id": null, + "image_url": "https://treetracker-dev-images.s3.eu-central-1.amazonaws.com/2020.10.19.09.50.38_-5.508172399749922_38.98146973686408_6bebe71e-5369-4ae0-8c47-9eeff6599fb0_IMG_20201019_094615_7537040365910944885.jpg", + "certificate_id": null, + "estimated_geometric_location": "0101000020E610000027ECE2CCA07D43407E9F76585E0816C0", + "lat": "-5.508172399749922", + "lon": "38.98146973686408", + "gps_accuracy": 4, + "active": true, + "planter_photo_url": "https://treetracker-dev-images.s3.eu-central-1.amazonaws.com/2020.10.19.09.47.53_-5.508107173727935_38.981361706266256_39f0cc9d-0f13-4547-8142-150f15cabb67_IMG_20201019_094513_6614320100195503436.jpg", + "device_id": null, + "sequence": null, + "note": "", + "verified": false, + "uuid": "38572a5b-ac5b-4a77-943a-72254376735e", + "approved": false, + "status": "planted", + "cluster_regions_assigned": true, + "species_id": null, + "planting_organization_id": 1, + "payment_id": null, + "contract_id": null, + "token_issued": false, + "morphology": null, + "age": null, + "species": null, + "capture_approval_tag": null, + "rejection_reason": null, + "matching_hash": null, + "device_identifier": "651f04008af0d91a", + "images": {}, + "domain_specific_data": {}, + "token_id": null, + "name": null, + "earnings_id": null, + "species_name": null, + "first_name": "Sebastian ", + "last_name": "Gaertner", + "user_image_url": "https://treetracker-production.nyc3.digitaloceanspaces.com/2019.07.10.18.32.42_b4fad89a-10b6-40cc-a134-0085d0e581d2_IMG_20190710_183201_8089920786231467340.jpg", + "token_uuid": null, + "wallet": null, + "attributes": { + "dbh": "13", + "abs_step_count": "75351", + "delta_step_count": "0", + "height_color": "yellow", + "app_build": "1.4.0", + "app_flavor": "justdiggit", + "app_version": "113" } - \ No newline at end of file +} diff --git a/cypress/fixtures/tree186734.json b/cypress/fixtures/tree186734.json index f4110776..ecbd8042 100644 --- a/cypress/fixtures/tree186734.json +++ b/cypress/fixtures/tree186734.json @@ -1,22 +1,19 @@ { - "id":"da4d3ed8-8655-44c5-a7ba-d4c45a0dfb10", - "latest_capture_id":"b74f65d8-bd25-4ca4-a4f7-8ad50ba9a936", - "image_url":"https://treetracker-production-images.s3.eu-central-1.amazonaws.com/2022.01.27.00.19.43_8.430052995999995_-13.204408322000003_8ad6e7a1-3b35-4998-80c9-faccaa9faaf1_IMG_20220125_120721_490991521.jpg", - "lat":"8.430052996", - "lon":"-13.204408322", - "estimated_geometric_location":"0101000020E61000005E0A2435A8682AC0CDBB02E82FDC2040", - "gps_accuracy":0, - "morphology":null, - "age":null,"created_at":"2022-01-25T12:06:36.000Z", - "updated_at":"2022-01-25T12:06:36.000Z", - "estimated_geographic_location":"0101000020E61000005E0A2435A8682AC0CDBB02E82FDC2040", - "status":"active", - "attributes":null, - "species_id":null, - "grower_id":940, - "name":"The Best Tree" - + "id": "da4d3ed8-8655-44c5-a7ba-d4c45a0dfb10", + "latest_capture_id": "b74f65d8-bd25-4ca4-a4f7-8ad50ba9a936", + "image_url": "https://treetracker-production-images.s3.eu-central-1.amazonaws.com/2022.01.27.00.19.43_8.430052995999995_-13.204408322000003_8ad6e7a1-3b35-4998-80c9-faccaa9faaf1_IMG_20220125_120721_490991521.jpg", + "lat": "8.430052996", + "lon": "-13.204408322", + "estimated_geometric_location": "0101000020E61000005E0A2435A8682AC0CDBB02E82FDC2040", + "gps_accuracy": 0, + "morphology": null, + "age": null, + "created_at": "2022-01-25T12:06:36.000Z", + "updated_at": "2022-01-25T12:06:36.000Z", + "estimated_geographic_location": "0101000020E61000005E0A2435A8682AC0CDBB02E82FDC2040", + "status": "active", + "attributes": null, + "species_id": null, + "grower_id": 940, + "name": "The Best Tree" } - - - diff --git a/cypress/tests/integration/growers/[growerid].cy.js b/cypress/tests/integration/growers/[growerid].cy.js index 4b54a87e..e0359b49 100644 --- a/cypress/tests/integration/growers/[growerid].cy.js +++ b/cypress/tests/integration/growers/[growerid].cy.js @@ -1,25 +1,56 @@ import grower from '../../../../doc/examples/growers/100.json'; +import tree from '../../../fixtures/tree186734.json'; import { prepareNocks, clearNocks } from '../nockRoutes'; +const getStubbedImageUrl = (image) => { + const blob = Cypress.Blob.base64StringToBlob(image, 'image/png'); + return Cypress.Blob.createObjectURL(blob); +}; + beforeEach(() => { clearNocks(); + + const stubs = {}; + cy.fixture('images/grower.png').then((image) => { + stubs.grower = { ...grower, image_url: getStubbedImageUrl(image) }; + }); + cy.fixture('images/trees/10.jpg').then((image) => { + stubs.tree = { ...tree, image_url: getStubbedImageUrl(image) }; + }); + cy.wrap(null).then(() => { + prepareNocks(stubs); + }); + + cy.visit(`/growers/${grower.id}`, { + failOnStatusCode: false, + }); }); describe('Grower Page', () => { it('renders with grower data', () => { - const path = `/growers/${grower.id}`; - cy.fixture('images/grower.png').then((image) => { - const blob = Cypress.Blob.base64StringToBlob(image, 'image/png'); - const image_url = Cypress.Blob.createObjectURL(blob); - prepareNocks({ grower: { ...grower, image_url } }); - }); + cy.url().should('include', `/growers/${grower.id}`); + cy.get('h6').eq(2).should('contain', 'Stanley K.'); + cy.get('h2').first().should('have.text', 'Stanley K.'); + cy.get('h2').eq(1).should('have.text', '1'); + cy.get('h2').eq(2).should('have.text', '1'); + cy.get('time').should('have.text', ' April 17, 2024'); + cy.get('h6').eq(5).should('have.text', 'Capture - da4d...fb10'); + cy.get('p').eq(13).should('have.text', grower.about); - cy.visit(path, { - failOnStatusCode: false, - }); + // check that grower and tree images are present, but stubbed + cy.get('img') + .eq(1) + .invoke('attr', 'src') + .should('include', 'blob:http://localhost:3000/'); + cy.get('img') + .eq(2) + .invoke('attr', 'src') + .should('include', 'blob:http://localhost:3000/'); + cy.get('img') + .eq(3) + .invoke('attr', 'src') + .should('include', 'localhost:3000/'); - cy.url().should('include', '/growers'); - cy.contains(grower.first_name); cy.screenshot(); }); }); diff --git a/cypress/tests/integration/moneytree.cy.js b/cypress/tests/integration/moneytree.cy.js index 2071d5ee..1efe820d 100644 --- a/cypress/tests/integration/moneytree.cy.js +++ b/cypress/tests/integration/moneytree.cy.js @@ -10,7 +10,9 @@ describe('Money Tree', () => { const path = `/trees/${tree.id}`; prepareNocks({ tree: { ...tree, species_name: 'Money Tree' } }); // nocksIntercept for extra data that I want to show up - cy.visit(path); + cy.visit(path, { + failOnStatusCode: false, + }); // misc code to confirm changes cy.contains(`${tree.id}`); }); diff --git a/doc/examples/growers/100.json b/doc/examples/growers/100.json index be5d9f6e..3a5e5ffd 100644 --- a/doc/examples/growers/100.json +++ b/doc/examples/growers/100.json @@ -20,7 +20,7 @@ "gender": "male", "about": "Stanley Kubrick is a visionary filmmaker known for his meticulous attention to detail, innovative storytelling, and exploration of profound philosophical and psychological themes across a diverse range of genres. He also tends to several trees.", "reference_id": 11, - "show_in_map": false, + "show_in_map": true, "links": { "featured_trees": "/trees?grower_id=100&limit=4", "associated_organizations": "/organizations?grower_id=100", diff --git a/src/pages/growers/[growerid].js b/src/pages/growers/[growerid].js index d7372e47..e9eab3d5 100644 --- a/src/pages/growers/[growerid].js +++ b/src/pages/growers/[growerid].js @@ -8,7 +8,7 @@ import Portal from '@mui/material/Portal'; import Typography from '@mui/material/Typography'; import moment from 'moment'; import { useRouter } from 'next/router'; -import { useEffect, useMemo, useState } from 'react'; +import { Fragment, useEffect, useMemo, useState } from 'react'; import Badge from 'components/Badge'; import CustomWorldMap from 'components/CustomWorldMap'; import FeaturedTreesSlider from 'components/FeaturedTreesSlider'; @@ -364,7 +364,6 @@ export default function Grower(props) { }} > -
Sam
{grower.associatedOrganizations.organizations.map((org) => ( - <> + - + ))}
- - - {!isFullscreen && false && ( - - - - )} + return ( + <> + + + {!isFullscreen && false && ( + + + + )} - {/* {!isFullscreen && } */} + {/* {!isFullscreen && } */} - - - , - name: 'Home', - url: '/', - }, - { - name: 'tree spotlight', - }, - ]} - /> - - - {captures?.length > 0 && ( - <> - - Featured captures this week - - {false && ( // going to be replaced by search filter component - ( - - ) - )} - - - - - )} - {organizations.length > 0 && ( - <> - - - Featured organizations this week - - `/organizations/${id}`} - color="primary" - planters={organizations} - isMobile={isFullscreen} + + , + name: 'Home', + url: '/', + }, + { + name: 'tree spotlight', + }, + ]} /> - - )} - {growers.length > 0 && ( - <> - - Featured planters this week - - `/planters/${id}`} - color="secondary" - planters={growers} - isMobile={isFullscreen} - /> - - )} - {wallets.length > 0 && ( - <> - Featured wallets this week - `/wallets/${id}`} - color="secondary" - planters={wallets} - isMobile={isFullscreen} /> - - )} - - Check out the global leaders in the tree planting effort - - [t.spacing(4, 0, 0, 0), t.spacing(8, 0, 0, 0)], - }} - > - { - setContinentTag(continent); + + {captures?.length > 0 && ( + <> + + Featured captures this week + + {false && ( // going to be replaced by search filter component + + + + )} + + + + + )} + {organizations.length > 0 && ( + <> + + + Featured organizations this week + + `/organizations/${id}`} + color="primary" + planters={organizations} + isMobile={isFullscreen} + /> + + )} + {growers.length > 0 && ( + <> + + Featured planters this week + + `/planters/${id}`} + color="secondary" + planters={growers} + isMobile={isFullscreen} + /> + + )} + {wallets.length > 0 && ( + <> + Featured wallets this week + `/wallets/${id}`} + color="secondary" + planters={wallets} + isMobile={isFullscreen} + /> + + )} + - - - - - {isFullscreen && ( - + > + Check out the global leaders in the tree planting effort + [t.spacing(4, 0, 0, 0), t.spacing(8, 0, 0, 0)], }} > - - Treetracker Spotlight - - - - )} - {isFullscreen && ( - - - Treetracker Spotlight + { + setContinentTag(continent); + }} + /> - - )} - ); + + + + {isFullscreen && ( + + + + Treetracker Spotlight + + + + )} + {isFullscreen && ( + + + Treetracker Spotlight + + + )} + + ); } async function serverSideData(params) {