From 2a32258a5422552c0b67e7814b8a6061ccf01054 Mon Sep 17 00:00:00 2001 From: Nicolas Dontigny Date: Sat, 23 Mar 2024 11:47:33 -0400 Subject: [PATCH 1/4] Started analytics page --- canopeum_frontend/src/pages/Analytics.tsx | 60 +++++++++++++++++++++-- 1 file changed, 56 insertions(+), 4 deletions(-) diff --git a/canopeum_frontend/src/pages/Analytics.tsx b/canopeum_frontend/src/pages/Analytics.tsx index 08b806765..bb569013c 100644 --- a/canopeum_frontend/src/pages/Analytics.tsx +++ b/canopeum_frontend/src/pages/Analytics.tsx @@ -1,10 +1,62 @@ -const Analytics = () => ( +import { useEffect, useState } from 'react' + +import type { SiteSummary } from '../services/api'; +import api from '../services/apiInterface'; +import { ensureError } from '../services/errors'; + +const Analytics = () => { + const [sites, setSites] = useState([]); + const [isLoading, setIsLoading] = useState(false); + const [error, setError] = useState(undefined); + + const fetchSites = async () => { + setIsLoading(true); + try { + const response = await api.sites.summary(); + console.log('ALL SITES response:', response); + setSites(response); + } catch (error_: unknown) { + setError(ensureError(error_)); + } finally { + setIsLoading(false); + } + }; + + useEffect((): void => { + void fetchSites(); + }, []); + + const renderSiteCards = () => + sites.map(site => ( +
+
+
{site.name ?? 'Unnamed site'}
+

+ Site content here. +

+
+
+ )) + + return (
-
-
-

Analytics

+
+
+

Manage my Sites

+ + +
+
+ {renderSiteCards()}
+
+

Average Annual Success

+
) + } export default Analytics From a3266fcaf026e0547f8a1f511eff6615e4170a66 Mon Sep 17 00:00:00 2001 From: Nicolas Dontigny Date: Sat, 23 Mar 2024 12:36:02 -0400 Subject: [PATCH 2/4] Displaying responsive cards & all sections --- canopeum_frontend/src/App.scss | 18 +++--- canopeum_frontend/src/pages/Analytics.tsx | 78 +++++++++++++++-------- 2 files changed, 59 insertions(+), 37 deletions(-) diff --git a/canopeum_frontend/src/App.scss b/canopeum_frontend/src/App.scss index f23dc6227..aba961d2e 100644 --- a/canopeum_frontend/src/App.scss +++ b/canopeum_frontend/src/App.scss @@ -15,27 +15,27 @@ body { } .navbar { - position: sticky; - top: 0; - z-index: 1000; - box-shadow: 0 0 10px 0 rgba(0, 0, 0, 0.1); - padding-bottom: 0; + position: sticky; + top: 0; + z-index: 1000; + box-shadow: 0 0 10px 0 rgba(0, 0, 0, 0.1); + padding-bottom: 0; } .navbar-logo { - padding-bottom: 0.3rem; + padding-bottom: 0.3rem; } .navbar li { - border-bottom: 0.3rem solid transparent; + border-bottom: 0.3rem solid transparent; } .nav-item.active { - border-bottom-color: white; + border-bottom-color: white; } .nav-item.active .material-symbols-outlined { - font-variation-settings: 'FILL' 1, 'wght' 700, 'GRAD' 0, 'opsz' 48; + font-variation-settings: 'FILL' 1, 'wght' 700, 'GRAD' 0, 'opsz' 48; } .fill-icon { diff --git a/canopeum_frontend/src/pages/Analytics.tsx b/canopeum_frontend/src/pages/Analytics.tsx index bb569013c..7092d18a0 100644 --- a/canopeum_frontend/src/pages/Analytics.tsx +++ b/canopeum_frontend/src/pages/Analytics.tsx @@ -5,20 +5,20 @@ import api from '../services/apiInterface'; import { ensureError } from '../services/errors'; const Analytics = () => { - const [sites, setSites] = useState([]); - const [isLoading, setIsLoading] = useState(false); + const [siteSummaries, setSiteSummaries] = useState([]); + // TODO(NicolasDontigny): Handle site summaries loading & error + const [isLoadingSites, setIsLoadingSites] = useState(false); const [error, setError] = useState(undefined); const fetchSites = async () => { - setIsLoading(true); + setIsLoadingSites(true); try { const response = await api.sites.summary(); - console.log('ALL SITES response:', response); - setSites(response); + setSiteSummaries(response); } catch (error_: unknown) { setError(ensureError(error_)); } finally { - setIsLoading(false); + setIsLoadingSites(false); } }; @@ -27,36 +27,58 @@ const Analytics = () => { }, []); const renderSiteCards = () => - sites.map(site => ( + siteSummaries.map(site => (
-
-
{site.name ?? 'Unnamed site'}
-

- Site content here. -

+
+
+
{site.name ?? 'Unnamed site'}
+

+ Site summary content here. +

+
)) + const renderSuccessRatesChart = () => ( +
+ Chart to be rendered here +
+ ) + return ( -
-
-
-

Manage my Sites

+
+
+
+

Manage my Sites

- -
-
- {renderSiteCards()} -
-
-

Average Annual Success

+ +
+ +
+ {renderSiteCards()}
+ +
+

Average Annual Success Rate Per Site

+ {renderSuccessRatesChart()} +
+ +
+
+

Batch Tracking

+
+ Filters Go Here +
+
+ {renderSuccessRatesChart()} +
+
-
-) - } + ) +} + export default Analytics From 480a700237d76a5a793341ad1e3b988be794d249 Mon Sep 17 00:00:00 2001 From: Nicolas Dontigny Date: Wed, 27 Mar 2024 16:42:41 -0400 Subject: [PATCH 3/4] Added site summary card translations --- canopeum_frontend/canopeum-mockoon.json | 2 +- .../src/components/analytics/SiteSummaryCard.tsx | 12 +++++++----- canopeum_frontend/src/locale/en/analytics.json | 8 +++++++- canopeum_frontend/src/locale/fr/analytics.json | 8 +++++++- 4 files changed, 22 insertions(+), 8 deletions(-) diff --git a/canopeum_frontend/canopeum-mockoon.json b/canopeum_frontend/canopeum-mockoon.json index 6db79df9a..f68d3c834 100644 --- a/canopeum_frontend/canopeum-mockoon.json +++ b/canopeum_frontend/canopeum-mockoon.json @@ -656,7 +656,7 @@ "responses": [ { "uuid": "ac5b893d-b30e-43c9-8988-4c13a7e0c819", - "body": "[\n {{#repeat 1 (int 1 14) comma=true}}\n {\n \"name\": \"{{lorem (int 1 3)}}\",\n \"type\": {\n \"id\": {{int 0 6}},\n \"en\": \"{{lorem (int 1 2)}}\",\n \"fr\": \"{{lorem (int 1 2)}}\"\n },\n \"plantCount\": {{int 0 5000}},\n \"survivedCount\": {{int 0 5000}},\n \"propagationCount\": {{int 0 500}},\n \"visitorCount\": {{int 0 500}},\n \"sponsors\": {{{someOf (array (lorem 2) (lorem 2) (lorem 2) (lorem 2) (lorem 2)) 0 5 true}}},\n \"progress\": {{float 0 100}}\n }\n {{/repeat}}\n]", + "body": "[\n {{#repeat 1 (int 1 14) comma=true}}\n {\n \"id\": \"{{int (int 1 9999)}}\", \"name\": \"{{lorem (int 1 3)}}\", \n \"type\": {\n \"id\": {{int 0 6}},\n \"en\": \"{{lorem (int 1 2)}}\",\n \"fr\": \"{{lorem (int 1 2)}}\"\n },\n \"plantCount\": {{int 0 5000}},\n \"survivedCount\": {{int 0 5000}},\n \"propagationCount\": {{int 0 500}},\n \"visitorCount\": {{int 0 500}},\n \"sponsors\": {{{someOf (array (lorem 2) (lorem 2) (lorem 2) (lorem 2) (lorem 2)) 0 5 true}}},\n \"progress\": {{float 0 100}}\n }\n {{/repeat}}\n]", "latency": 0, "statusCode": 200, "label": "", diff --git a/canopeum_frontend/src/components/analytics/SiteSummaryCard.tsx b/canopeum_frontend/src/components/analytics/SiteSummaryCard.tsx index 9bc892367..110fb9c28 100644 --- a/canopeum_frontend/src/components/analytics/SiteSummaryCard.tsx +++ b/canopeum_frontend/src/components/analytics/SiteSummaryCard.tsx @@ -7,7 +7,7 @@ type Props = { } const SiteSummaryCard = ({ site }: Props) => { - const { t } = useTranslation() + const { t: translate } = useTranslation() return (
{ psychiatry
{site.plantCount} - Planted + {translate('analytics.site-summary.planted')}
@@ -50,7 +50,7 @@ const SiteSummaryCard = ({ site }: Props) => { forest
{site.survivedCount} - Survived + {translate('analytics.site-summary.survived')}
@@ -58,7 +58,7 @@ const SiteSummaryCard = ({ site }: Props) => { forest
{site.propagationCount} - Propagation + {translate('analytics.site-summary.propagation')}
@@ -74,7 +74,9 @@ const SiteSummaryCard = ({ site }: Props) => { />
- {Math.round(site.progress)}% Sponsored + + {Math.round(site.progress)}% {translate('analytics.site-summary.sponsored')} + diff --git a/canopeum_frontend/src/locale/en/analytics.json b/canopeum_frontend/src/locale/en/analytics.json index 084c28060..2124fe0ab 100644 --- a/canopeum_frontend/src/locale/en/analytics.json +++ b/canopeum_frontend/src/locale/en/analytics.json @@ -16,5 +16,11 @@ "table-row-11": "how many replaced", "table-row-12": "amount of seeds collected in area", "table-row-13": "types of seeds collected", - "last-update": "last update" + "last-update": "last update", + "site-summary": { + "planted": "Planted", + "survived": "Survived", + "propagation": "Propagation", + "sponsored": "Sponsored" + } } diff --git a/canopeum_frontend/src/locale/fr/analytics.json b/canopeum_frontend/src/locale/fr/analytics.json index 782126df6..2ed0d2ac5 100644 --- a/canopeum_frontend/src/locale/fr/analytics.json +++ b/canopeum_frontend/src/locale/fr/analytics.json @@ -16,5 +16,11 @@ "table-row-11": "combien ont été remplacés", "table-row-12": "quantité de graines collectées dans la zone", "table-row-13": "types de graines collectées", - "last-update": "dernière mise à jour" + "last-update": "dernière mise à jour", + "site-summary": { + "planted": "Planté", + "survived": "Survécu", + "propagation": "Propagation", + "sponsored": "Sponsorisé" + } } From 05a003a45552dd78ca86b1933d9247af60931dc5 Mon Sep 17 00:00:00 2001 From: Nicolas Dontigny Date: Wed, 27 Mar 2024 17:02:29 -0400 Subject: [PATCH 4/4] Added basic site details layout + translations --- .../src/locale/en/analyticsSite.json | 4 ++ canopeum_frontend/src/locale/en/index.ts | 2 + .../src/locale/fr/analyticsSite.json | 4 ++ canopeum_frontend/src/locale/fr/index.ts | 2 + canopeum_frontend/src/pages/AnalyticsSite.tsx | 38 ++++++++++++++++++- 5 files changed, 48 insertions(+), 2 deletions(-) create mode 100644 canopeum_frontend/src/locale/en/analyticsSite.json create mode 100644 canopeum_frontend/src/locale/fr/analyticsSite.json diff --git a/canopeum_frontend/src/locale/en/analyticsSite.json b/canopeum_frontend/src/locale/en/analyticsSite.json new file mode 100644 index 000000000..6cd3f30fb --- /dev/null +++ b/canopeum_frontend/src/locale/en/analyticsSite.json @@ -0,0 +1,4 @@ +{ + "location": "Location", + "batch-tracking": "Batch Tracking" +} diff --git a/canopeum_frontend/src/locale/en/index.ts b/canopeum_frontend/src/locale/en/index.ts index f995f17d8..f298365b8 100644 --- a/canopeum_frontend/src/locale/en/index.ts +++ b/canopeum_frontend/src/locale/en/index.ts @@ -1,4 +1,5 @@ import analyticsJSON from './analytics.json' +import analyticsSiteJSON from './analyticsSite.json' import homeJSON from './home.json' import mapSiteJSON from './mapSite.json' @@ -6,6 +7,7 @@ const enJSON = { translation: { home: { ...homeJSON }, analytics: { ...analyticsJSON }, + analyticsSite: { ...analyticsSiteJSON }, mapSite: { ...mapSiteJSON }, }, } diff --git a/canopeum_frontend/src/locale/fr/analyticsSite.json b/canopeum_frontend/src/locale/fr/analyticsSite.json new file mode 100644 index 000000000..869fd68f3 --- /dev/null +++ b/canopeum_frontend/src/locale/fr/analyticsSite.json @@ -0,0 +1,4 @@ +{ + "location": "Localisation", + "batch-tracking": "Suivi des lots" +} diff --git a/canopeum_frontend/src/locale/fr/index.ts b/canopeum_frontend/src/locale/fr/index.ts index 3bd6153ed..114b5dd72 100644 --- a/canopeum_frontend/src/locale/fr/index.ts +++ b/canopeum_frontend/src/locale/fr/index.ts @@ -1,4 +1,5 @@ import analyticsJSON from './analytics.json' +import analyticsSiteJSON from './analyticsSite.json' import homeJSON from './home.json' import mapSiteJSON from './mapSite.json' @@ -6,6 +7,7 @@ const frJSON = { translation: { home: { ...homeJSON }, analytics: { ...analyticsJSON }, + analyticsSite: { ...analyticsSiteJSON }, mapSite: { ...mapSiteJSON }, }, } diff --git a/canopeum_frontend/src/pages/AnalyticsSite.tsx b/canopeum_frontend/src/pages/AnalyticsSite.tsx index 9272b7476..660a88f18 100644 --- a/canopeum_frontend/src/pages/AnalyticsSite.tsx +++ b/canopeum_frontend/src/pages/AnalyticsSite.tsx @@ -1,9 +1,43 @@ +import type { Site } from '@services/api' +import api from '@services/apiInterface' +import { useEffect, useState } from 'react' import { useTranslation } from 'react-i18next' +import { useParams } from 'react-router-dom' const AnalyticsSite = () => { - const { t } = useTranslation() + const { t: translate } = useTranslation() + const { siteId: siteIdFromParams } = useParams() - return
+ const [site, setSite] = useState() + + const fetchSite = async (siteId: number) => setSite(await api().analytics.site(siteId)) + + useEffect(() => { + if (!siteIdFromParams) return + + const siteIdNumber = Number.parseInt(siteIdFromParams, 10) + if (!siteIdNumber) return + + void fetchSite(siteIdNumber) + }, [siteIdFromParams]) + + return ( +
+
+
+ {translate('analyticsSite.location')} +
+ +
+

{site?.name}

+
+
+ +
+

{translate('analyticsSite.batch-tracking')}

+
+
+ ) } export default AnalyticsSite