- {org.name.split(":")[0]}
+
+ {org.name.split(':')[0]}
{org.count && (
@@ -33,8 +38,8 @@ function OrgsLegendItem({
)}
>
- );
- }, [org.slug, searchParams]);
+ )
+ }, [org.slug, searchParams])
if (org.isMain) {
return (
@@ -44,8 +49,8 @@ function OrgsLegendItem({
{org.name}
- );
+ )
}
return (
@@ -73,7 +78,7 @@ function OrgsLegendItem({
- );
+ )
}
-export default memo(OrgsLegendItem);
+export default memo(OrgsLegendItem)
diff --git a/frontend-nextjs/src/utility/orgsUtil.ts b/frontend-nextjs/src/utility/orgsUtil.ts
index fbe85e3..d33fb0e 100644
--- a/frontend-nextjs/src/utility/orgsUtil.ts
+++ b/frontend-nextjs/src/utility/orgsUtil.ts
@@ -10,7 +10,7 @@ export function getOrgStats(data: {
organisation: OrganisationType;
}) {
const { events, organisations, organisation: org } = data;
- const orgEvents = data.events.filter((e) =>
+ const orgEvents = events.filter((e) =>
e.organizers.find((o) => o.slug === org.slug),
);
const totalEvents = orgEvents.length ?? 0;
@@ -23,7 +23,7 @@ export function getOrgStats(data: {
.reduce((acc, e) => {
for (const partner of e.organizers) {
if (partner.slug === org.slug) continue;
- const partnerOrg = data.organisations.find(
+ const partnerOrg = organisations.find(
(o) => o.slug === partner.slug,
);
if (!partnerOrg) continue;
@@ -33,16 +33,14 @@ export function getOrgStats(data: {
}, new Map())
.values(),
);
+
return {
...org,
slug: org.slug,
name: org.name,
totalEvents: totalEvents,
totalParticipants,
- avgParticipantsPerEvent:
- totalEvents === 0 ? 0 : totalParticipants / totalEvents,
- avgPartnerOrgsPerEvent:
- totalEvents === 0 ? 0 : partners.length / totalEvents,
+ avgParticipantsPerEvent: Math.ceil(totalParticipants / Math.max(1, totalEvents)),
totalPartners: partners.length,
partners,
};
diff --git a/frontend-nextjs/src/utility/textUtil.tsx b/frontend-nextjs/src/utility/textUtil.tsx
index 664b6eb..c9c4b13 100644
--- a/frontend-nextjs/src/utility/textUtil.tsx
+++ b/frontend-nextjs/src/utility/textUtil.tsx
@@ -1,263 +1,268 @@
-import type { ReactNode } from "react";
-import { cn } from "./classNames";
+import type { ReactNode } from 'react'
+import { cn } from './classNames'
export function titleCase(str: string) {
return str.replace(
/\w\S*/g,
(txt) => txt.charAt(0).toUpperCase() + txt.substr(1),
- );
+ )
}
type ImpactDescriptionProps = {
- isSentiment: boolean;
- topicNode: ReactNode;
- isIncreasing: boolean;
- leastBound: string;
- mostBound: string;
-};
+ isSentiment: boolean
+ topicNode: ReactNode
+ isIncreasing: boolean
+ leastBound: string
+ mostBound: string
+}
-type WrapperEl = ({ children }: { children: ReactNode }) => JSX.Element;
+type WrapperEl = ({ children }: { children: ReactNode }) => JSX.Element
const B: WrapperEl = ({ children }) => (
{children}
-);
+)
const textsEnGB = {
- language: "en-GB",
+ language: 'en-GB',
seo: {
- siteTitle: "Media Impact Monitor",
- siteDescription: "A collaborative project aimed at enabling protest groups and NGOs to evaluate their impact on public discourse.",
+ siteTitle: 'Media Impact Monitor',
+ siteDescription:
+ 'A collaborative project aimed at enabling protest groups and NGOs to evaluate their impact on public discourse.',
},
mainNavigation: {
- home: "Home",
- dashboard: "Dashboard",
- organisations: "Organisations",
- about: "About",
- docs: "Docs",
- fourOFour: "Page not found",
- logoAssets: "Logo assets",
+ home: 'Home',
+ dashboard: 'Dashboard',
+ organisations: 'Organisations',
+ about: 'About',
+ docs: 'Docs',
+ fourOFour: 'Page not found',
+ logoAssets: 'Logo assets',
themeToggle: {
- light: "Light",
- dark: "Dark",
- system: "System",
- toggleText: "Toggle theme",
+ light: 'Light',
+ dark: 'Dark',
+ system: 'System',
+ toggleText: 'Toggle theme',
},
},
fourOFour: {
- heading: "This page does not exist",
- description: "It seems that the page you were looking for does not exist. Try the dashboard instead.",
+ heading: 'This page does not exist',
+ description:
+ 'It seems that the page you were looking for does not exist. Try the dashboard instead.',
},
errors: {
- heading: "Error",
+ heading: 'Error',
buttons: {
- tryAgain: "Try again",
- resetFilters: "Reset all filters",
+ tryAgain: 'Try again',
+ resetFilters: 'Reset all filters',
},
errorLoadingChartData: (
- { chartName = "chart" }: { chartName?: string } = { chartName: "chart" },
+ { chartName = 'chart' }: { chartName?: string } = { chartName: 'chart' },
) => `Error fetching the ${chartName} impact data`,
apiErrorTranslations: {
defaultMessage: (
- { datasetName = "data" }: { datasetName?: string } = {
- datasetName: "data",
+ { datasetName = 'data' }: { datasetName?: string } = {
+ datasetName: 'data',
},
) =>
`There was unexpected issue while retrieving the ${datasetName}. Please try again in a few minutes.`,
ApiFetchError: (
- { datasetName = "data" }: { datasetName?: string } = {
- datasetName: "data",
+ { datasetName = 'data' }: { datasetName?: string } = {
+ datasetName: 'data',
},
) =>
`We are facing issues with our API and where not able to retrieve the ${datasetName}. Please try again in a few minutes.`,
ZodError: (
- { datasetName = "data" }: { datasetName?: string } = {
- datasetName: "data",
+ { datasetName = 'data' }: { datasetName?: string } = {
+ datasetName: 'data',
},
) =>
`The ${datasetName} returned from the API are/is not in the expected format. Please try again in a few minutes or contact the developers.`,
- "An error has occured: 500": (
- { datasetName = "data" }: { datasetName?: string } = {
- datasetName: "data",
+ 'An error has occured: 500': (
+ { datasetName = 'data' }: { datasetName?: string } = {
+ datasetName: 'data',
},
- ) => "There was an unecpected error: 500",
+ ) => 'There was an unecpected error: 500',
},
},
footer: {
links: {
- logoAssets: "Logo assets",
- gitHub: "GitHub",
- appStatus: "App status",
+ logoAssets: 'Logo assets',
+ gitHub: 'GitHub',
+ appStatus: 'App status',
},
copyRightOwner: `Social Change Lab`,
- hostedBy: "Hosted by",
- sponsoredBy: "Sponsored by",
+ hostedBy: 'Hosted by',
+ sponsoredBy: 'Sponsored by',
},
homepage: {
hero: {
- heading: "Welcome to the Media Impact Monitor",
+ heading: 'Welcome to the Media Impact Monitor',
text: [
- "The Media Impact Monitor is a collaborative project aimed at enabling protest groups and NGOs to evaluate their impact on public discourse.",
- "Through the examination of various media sources, from local and national newspapers to social media and parliamentary debates, the tool provides a detailed view of how activism influences public discussion.",
+ 'The Media Impact Monitor is a collaborative project aimed at enabling protest groups and NGOs to evaluate their impact on public discourse.',
+ 'Through the examination of various media sources, from local and national newspapers to social media and parliamentary debates, the tool provides a detailed view of how activism influences public discussion.',
],
buttons: {
- goToDashboard: "Go to the dashboard",
- about: "About",
- docs: "Documentation",
+ goToDashboard: 'Go to the dashboard',
+ about: 'About',
+ docs: 'Documentation',
},
newsletterCallout: {
- heading: "Get notified when we launch the v1!",
+ heading: 'Get notified when we launch the v1!',
},
backgroundImage: {
- lightAlt: "A screenshot of the dashboard (light mode)",
- darkAlt: "A screenshot of the dashboard (dark mode)",
+ lightAlt: 'A screenshot of the dashboard (light mode)',
+ darkAlt: 'A screenshot of the dashboard (dark mode)',
},
},
},
newsLetterSection: {
- heading: "Receive updates and get notified when we launch the v1!",
- screenshotLightAlt: "A screenshot of the dashboard (light mode)",
- screenshotDarkAlt: "A screenshot of the dashboard (dark mode)",
- invalidEmail: "Please enter a valid email address",
- inputLabel: "What is your email address?",
- inputPlaceholder: "anna.smith@example.com",
- submitButton: "Subscribe",
+ heading: 'Receive updates and get notified when we launch the v1!',
+ screenshotLightAlt: 'A screenshot of the dashboard (light mode)',
+ screenshotDarkAlt: 'A screenshot of the dashboard (dark mode)',
+ invalidEmail: 'Please enter a valid email address',
+ inputLabel: 'What is your email address?',
+ inputPlaceholder: 'anna.smith@example.com',
+ submitButton: 'Subscribe',
},
uiCommon: {
- showMore: "Show more",
- showLess: "Show less",
- nextPageAriaLabel: "Go to next page",
- prevPageAriaLabel: "Go to previous page",
+ showMore: 'Show more',
+ showLess: 'Show less',
+ nextPageAriaLabel: 'Go to next page',
+ prevPageAriaLabel: 'Go to previous page',
},
filters: {
mediaSource: {
- label: "Media source",
- fieldPlaceholder: "Select data source",
+ label: 'Media source',
+ fieldPlaceholder: 'Select data source',
values: {
onlineNews: {
- name: "Online News",
- description: "Articles from online news pages.",
+ name: 'Online News',
+ description: 'Articles from online news pages.',
links: [
{
- label: "Official Website",
- href: "https://www.mediacloud.org/",
+ label: 'Official Website',
+ href: 'https://www.mediacloud.org/',
},
],
},
printNews: {
- name: "Print News",
- description: "Articles from print newspapers.",
+ name: 'Print News',
+ description: 'Articles from print newspapers.',
links: [
{
- label: "Official Website",
- href: "https://www.genios.de/",
+ label: 'Official Website',
+ href: 'https://www.genios.de/',
},
{
- label: "Press Page",
- href: "https://www.genios.de/browse/Alle/Presse",
+ label: 'Press Page',
+ href: 'https://www.genios.de/browse/Alle/Presse',
},
],
},
googleTrends: {
- name: "Google Trends",
- description: "Search trends from Google.",
+ name: 'Google Trends',
+ description: 'Search trends from Google.',
links: [
{
- label: "Official Website",
- href: "https://trends.google.com/trends/",
+ label: 'Official Website',
+ href: 'https://trends.google.com/trends/',
},
],
},
},
},
organisations: {
- label: "Organisations",
- selectOrganisation: "Select organisation",
- noOrganisationsFound: "No organisations found",
- toggleAllNone: "Toggle all/none",
- unknownOrganisation: "Unknown organisation",
+ label: 'Organisations',
+ selectOrganisation: 'Select organisation',
+ noOrganisationsFound: 'No organisations found',
+ toggleAllNone: 'Toggle all/none',
+ unknownOrganisation: 'Unknown organisation',
},
timeRange: {
- label: "Time range",
- presets: "Presets",
- last6MonthsLong: "Last 6 months",
- last6MonthsShort: "-6M",
- last12MonthsLong: "Last 12 months",
- last12MonthsShort: "-12M",
- last30DaysLong: "Last 30 days",
- last30DaysShort: "-30D",
+ label: 'Time range',
+ presets: 'Presets',
+ last6MonthsLong: 'Last 6 months',
+ last6MonthsShort: '-6M',
+ last12MonthsLong: 'Last 12 months',
+ last12MonthsShort: '-12M',
+ last30DaysLong: 'Last 30 days',
+ last30DaysShort: '-30D',
buttons: {
- apply: "Apply",
- cancel: "Cancel",
- resetDefaults: "Reset defaults",
+ apply: 'Apply',
+ cancel: 'Cancel',
+ resetDefaults: 'Reset defaults',
},
},
},
info: {
welcome_message: {
- heading: "The Impact Monitor Dashboard",
+ heading: 'The Impact Monitor Dashboard',
description: [
- "Welcome to the Impact Monitor Dashboard. Here, you can see protests over time, topics, and sentiments within the media and how protest organisations impact the media landscape.",
- "Start by setting the filters at the top of the page and scroll down to explore the data.",
+ 'Welcome to the Impact Monitor Dashboard. Here, you can see protests over time, topics, and sentiments within the media and how protest organisations impact the media landscape.',
+ 'Start by setting the filters at the top of the page and scroll down to explore the data.',
],
buttons: {
- cta: "Got it",
- docs: "Learn more",
- whatIsThis: "What is this dashboard?",
+ cta: 'Got it',
+ docs: 'Learn more',
+ whatIsThis: 'What is this dashboard?',
},
arrowHints: {
- setYourFilters: "Set your filters",
- scrollDown: "Scroll down to explore",
+ setYourFilters: 'Set your filters',
+ scrollDown: 'Scroll down to explore',
},
},
},
charts: {
help: {
- howToReadThis: "How to read this",
- readInTheDocs: "Read in the docs",
+ howToReadThis: 'How to read this',
+ readInTheDocs: 'Read in the docs',
tabs: {
- info: "Info",
- methodology: "Methodology",
- data: "Data",
+ info: 'Info',
+ methodology: 'Methodology',
+ data: 'Data',
},
},
common: {
- legend: "Legend",
- dataCredit: "Data credit",
- articlesUnit: "articles",
- total: "Total",
- loading: "Loading chart data...",
+ legend: 'Legend',
+ dataCredit: 'Data credit',
+ articlesUnit: 'articles',
+ total: 'Total',
+ loading: 'Loading chart data...',
cantShowThisChart: {
heading: "We can't show this chart",
- text: "Given the the following limitations:",
+ text: 'Given the the following limitations:',
limitationTranslations: {
// CAUTION: The text below is the the original message from the API
// used as s key to translate it. Only change the value on the right
// of the colon ":"
- "Sentiment trend requires fulltext analysis, which is only available for news_online, not web_google.": `This chart can only be displayed when selecting the meda source "Online News". The source "Google Trends" is not supported.`,
- "Sentiment trend requires fulltext analysis, which is only available for news_online, not news_print.": `This chart can only be displayed when selecting the meda source "Online News". The source "Print News" is not supported.`,
+ 'Sentiment trend requires fulltext analysis, which is only available for news_online, not web_google.': `This chart can only be displayed when selecting the meda source "Online News". The source "Google Trends" is not supported.`,
+ 'Sentiment trend requires fulltext analysis, which is only available for news_online, not news_print.': `This chart can only be displayed when selecting the meda source "Online News". The source "Print News" is not supported.`,
},
},
},
topics: {
- positive: "Positive",
- negative: "Negative",
- neutral: "Neutral",
- "climate activism": "Climate Activism",
- "climate crisis framing": "Climate Crisis Framing",
- "climate policy": "Climate Policy",
- "climate science": "Climate Science",
+ positive: 'Positive',
+ negative: 'Negative',
+ neutral: 'Neutral',
+ 'climate activism': 'Climate Activism',
+ 'climate crisis framing': 'Climate Crisis Framing',
+ 'climate policy': 'Climate Policy',
+ 'climate science': 'Climate Science',
},
aggregationUnit: {
- day: "day",
- week: "week",
- month: "month",
- year: "year",
+ day: 'day',
+ week: 'week',
+ month: 'month',
+ year: 'year',
},
keywordsTooltip: {
intro: ({
categoryNode,
keywordsCount,
- }: { categoryNode: ReactNode; keywordsCount: number | string }) => (
+ }: {
+ categoryNode: ReactNode
+ keywordsCount: number | string
+ }) => (
<>
The category {categoryNode} encompasses articles including one or more
of the following {keywordsCount} keywords:
@@ -275,21 +280,21 @@ const textsEnGB = {
),
linkToDocs: ({ LinkWrapper }: { LinkWrapper: WrapperEl }) => (
<>
- To know more about the methodology, see the{" "}
+ To know more about the methodology, see the{' '}
documentation.
>
),
},
impact: {
buttons: {
- computeImpacts: "Compute impacts",
- hideComputedImpacts: "Hide computed impacts",
+ computeImpacts: 'Compute impacts',
+ hideComputedImpacts: 'Hide computed impacts',
},
tooltips: {
- upTo: "Up to",
- downTo: "Down to",
- atLeast: "At least",
- noImpact: "No impact",
+ upTo: 'Up to',
+ downTo: 'Down to',
+ atLeast: 'At least',
+ noImpact: 'No impact',
},
descriptions: {
unclearChange: ({ isSentiment, topicNode }: ImpactDescriptionProps) => (
@@ -311,12 +316,12 @@ const textsEnGB = {
}: ImpactDescriptionProps) => (
<>
{`shows ${isIncreasing ? `an ` : `a `}`}
- {isIncreasing ? "increase" : "decrease"}
+ {isIncreasing ? 'increase' : 'decrease'}
{` in the publication of `}
{isSentiment && <> {topicNode} >}
{` articles `}
{!isSentiment && <>about {topicNode}>}
- {" by at least "}
+ {' by at least '}
{leastBound}
{` and up to `}
{mostBound}
@@ -335,44 +340,47 @@ const textsEnGB = {
),
},
limitation: {
- title: "Limitation",
+ title: 'Limitation',
message: ({ organisationNode }: { organisationNode: ReactNode }) => (
<>
The impact of an average protest by {organisationNode} cannot be
computed because of the following limitations:
>
),
- widenYourFilters: "Widen your filters or choose another organisation.",
+ widenYourFilters: 'Widen your filters or choose another organisation.',
limitationTranslations: {
- "Not enough events to estimate impact.":
- "Not enough events to estimate impact.",
+ 'Not enough events to estimate impact.':
+ 'Not enough events to estimate impact.',
},
},
error: {
message:
- "The impact cannot be computed because of the following error:",
+ 'The impact cannot be computed because of the following error:',
changeYourFilters:
- "Change your filters or choose another organisation.",
+ 'Change your filters or choose another organisation.',
},
introduction: {
message: ({
organisationNode,
selectedTimeFrameNode,
}: {
- organisationNode: ReactNode;
- selectedTimeFrameNode: ReactNode;
+ organisationNode: ReactNode
+ selectedTimeFrameNode: ReactNode
}) => (
<>
- An average protest by {organisationNode} within the{" "}
+ An average protest by {organisationNode} within the{' '}
{selectedTimeFrameNode}
>
),
selectedTimeFrame: {
- label: "selected timeframe",
+ label: 'selected timeframe',
tooltipMessage: ({
fromNode,
toNode,
- }: { fromNode: ReactNode; toNode: ReactNode }) => (
+ }: {
+ fromNode: ReactNode
+ toNode: ReactNode
+ }) => (
<>
The selected timeframe is:
{fromNode} to {toNode}.
>
@@ -380,7 +388,10 @@ const textsEnGB = {
tooltipNotice: ({
percentageNode,
organisationNode,
- }: { percentageNode: ReactNode; organisationNode: ReactNode }) => (
+ }: {
+ percentageNode: ReactNode
+ organisationNode: ReactNode
+ }) => (
<>
Only {percentageNode} of protests from {organisationNode} are
within the selected timeframe. Select a longer timeframe to get a
@@ -391,35 +402,35 @@ const textsEnGB = {
},
},
protest_timeline: {
- heading: "What protests are happening?",
+ heading: 'What protests are happening?',
description: [
- "See protests over time for all of the selected organisations.",
- "Hover or click on the bubbles for more information on individual protest events.",
- "Currently, we only cover climate protests in Germany since 2020.",
+ 'See protests over time for all of the selected organisations.',
+ 'Hover or click on the bubbles for more information on individual protest events.',
+ 'Currently, we only cover climate protests in Germany since 2020.',
],
data_credit: [
{
- label: "Protest data",
+ label: 'Protest data',
links: [
{
- text: "Armed Conflict Location & Event Data Project (ACLED)",
- url: "https://acleddata.com/",
+ text: 'Armed Conflict Location & Event Data Project (ACLED)',
+ url: 'https://acleddata.com/',
},
],
},
],
legend: {
- size: "Size",
+ size: 'Size',
participants: {
- day: "Protest participants",
- week: "Weekly participants",
- month: "Monthly participants",
- year: "Yearly participants",
+ day: 'Protest participants',
+ week: 'Weekly participants',
+ month: 'Monthly participants',
+ year: 'Yearly participants',
},
- zeroOrUnknown: "0 or unknown",
- color: "Color",
- organisations: "Organisations",
- other: "Other",
+ zeroOrUnknown: '0 or unknown',
+ color: 'Color',
+ organisations: 'Organisations',
+ other: 'Other',
},
tooltips: {
aggregated: ({
@@ -429,45 +440,45 @@ const textsEnGB = {
participantCount,
orgsCount,
}: {
- timeUnitLabel: string;
- timeValue: string;
- protestCount: number;
- participantCount: number | undefined;
- orgsCount: number;
+ timeUnitLabel: string
+ timeValue: string
+ protestCount: number
+ participantCount: number | undefined
+ orgsCount: number
}) => (
<>
- The {timeUnitLabel} of {timeValue} saw a total of{" "}
+ The {timeUnitLabel} of {timeValue} saw a total of{' '}
- {protestCount.toLocaleString("en-GB")} protest
- {protestCount > 1 && "s"}
+ {protestCount.toLocaleString('en-GB')} protest
+ {protestCount > 1 && 's'}
{participantCount && (
<>
- , comprising of{" "}
+ , comprising of{' '}
- {participantCount.toLocaleString("en-GB")} participant
- {participantCount > 1 && "s"}
+ {participantCount.toLocaleString('en-GB')} participant
+ {participantCount > 1 && 's'}
>
)}
- {`, and ${protestCount > 1 ? "were" : "was"} organized by the following organisation${orgsCount > 1 ? "s" : ""}:`}
+ {`, and ${protestCount > 1 ? 'were' : 'was'} organized by the following organisation${orgsCount > 1 ? 's' : ''}:`}
>
),
},
},
topics_trend: {
- heading: "What topics are the focus of public discourse?",
+ heading: 'What topics are the focus of public discourse?',
description: [
- "See how many articles are published on various topics over time.",
- "Use the filters to switch between online newspaper articles, print newspaper articles, and queries that people search for on Google.",
+ 'See how many articles are published on various topics over time.',
+ 'Use the filters to switch between online newspaper articles, print newspaper articles, and queries that people search for on Google.',
],
data_credit: [
{
- label: "Media data",
+ label: 'Media data',
links: [
{
- text: "MediaCloud",
- url: "https://mediacloud.org/",
+ text: 'MediaCloud',
+ url: 'https://mediacloud.org/',
},
],
},
@@ -475,87 +486,86 @@ const textsEnGB = {
},
topics_impact: {
- heading: "Computed impacts",
+ heading: 'Computed impacts',
description:
- "See how a protest by an organisation influences the publication of articles about climate change (by topics).",
+ 'See how a protest by an organisation influences the publication of articles about climate change (by topics).',
},
sentiment_protest: {
- heading: "What sentiment does the media show towards the protests?",
+ heading: 'What sentiment does the media show towards the protests?',
description: [
"See whether the media's coverage of the protests is more positive, negative, or neutral.",
],
data_credit: [
{
- label: "Media data",
+ label: 'Media data',
links: [
{
- text: "MediaCloud",
- url: "https://mediacloud.org/",
+ text: 'MediaCloud',
+ url: 'https://mediacloud.org/',
},
],
},
],
},
sentiment_protest_impact: {
- heading: "Computed impacts",
+ heading: 'Computed impacts',
description:
- "See how a protest by an organisation influences the publication of articles about climate activism (by sentiment).",
+ 'See how a protest by an organisation influences the publication of articles about climate activism (by sentiment).',
},
sentiment_policy: {
heading:
- "What stance does the media have towards progressive climate policies?",
+ 'What stance does the media have towards progressive climate policies?',
description: [
- "See whether the media supports or opposes policies aimed at mitigating climate change.",
+ 'See whether the media supports or opposes policies aimed at mitigating climate change.',
],
data_credit: [
{
- label: "Media data",
+ label: 'Media data',
links: [
{
- text: "MediaCloud",
- url: "https://mediacloud.org/",
+ text: 'MediaCloud',
+ url: 'https://mediacloud.org/',
},
],
},
],
},
sentiment_policy_impact: {
- heading: "Computed impacts",
+ heading: 'Computed impacts',
description:
- "See how a protest by an organisation influences the publication of articles about climate policies (by sentiment).",
+ 'See how a protest by an organisation influences the publication of articles about climate policies (by sentiment).',
},
},
organisationsPage: {
- heading: "Organisations Overview",
+ heading: 'Organisations Overview',
description: ({
isSameDay,
formattedFrom,
formattedTo,
orgsCount,
}: {
- isSameDay: boolean;
- formattedFrom: string;
- formattedTo: string;
- orgsCount: number;
+ isSameDay: boolean
+ formattedFrom: string
+ formattedTo: string
+ orgsCount: number
}) => {
const formattedDate = isSameDay
? `on ${formattedFrom}`
- : `between ${formattedFrom} and ${formattedTo}`;
+ : `between ${formattedFrom} and ${formattedTo}`
const organisersCount =
orgsCount > 0
- ? `the ${orgsCount} selected organisation${orgsCount > 1 ? "s" : ""}`
- : "all organisations";
- return `An overview of ${organisersCount} with protests ${formattedDate}. You can use the filters above to change the date range or select specific organisations.`;
+ ? `the ${orgsCount} selected organisation${orgsCount > 1 ? 's' : ''}`
+ : 'all organisations'
+ return `An overview of ${organisersCount} with protests ${formattedDate}. You can use the filters above to change the date range or select specific organisations.`
},
- allOrganisations: "All organisations",
- showPartnersAriaLabel: "Show partners tooltip",
+ allOrganisations: 'All organisations',
+ showPartnersAriaLabel: 'Show partners tooltip',
propertyNames: {
- name: "Name",
- totalEvents: "Total Events",
- totalParticipants: "Total Participants",
- avgParticipants: "Avg. Participants",
- avgPartners: "Avg. Partners",
- totalPartners: "Total Partners",
+ name: 'Name',
+ totalEvents: 'Total Events',
+ totalParticipants: 'Total Participants',
+ avgParticipants: 'Avg. Participants',
+ totalPartners: 'Partners',
},
},
singleProtestPage: {
@@ -564,280 +574,283 @@ const textsEnGB = {
orgsCount,
orgName,
}: {
- formattedDate: string;
- orgsCount: number;
- orgName: string;
+ formattedDate: string
+ orgsCount: number
+ orgName: string
}) =>
cn(
`Protest on ${formattedDate}`,
- orgsCount > 1 && "multiple organisations",
+ orgsCount > 1 && 'multiple organisations',
orgsCount === 1 && orgName,
),
propertyNames: {
- city: "City",
- country: "Country",
- organisations: "Organisations",
+ city: 'City',
+ country: 'Country',
+ organisations: 'Organisations',
},
table: {
- heading: "Protest Articles",
- description: "See which articles mention the protest",
+ heading: 'Protest Articles',
+ description: 'See which articles mention the protest',
header: {
- title: "Title",
- summary: "Summary",
- date: "Date",
- url: "URL",
- sentimentActivism: "Sent. Activism",
- sentimentPolicy: "Sent. Policy",
+ title: 'Title',
+ summary: 'Summary',
+ date: 'Date',
+ url: 'URL',
+ sentimentActivism: 'Sent. Activism',
+ sentimentPolicy: 'Sent. Policy',
},
},
charts: {
- heading: "Protest Timeline of Sentiment",
+ heading: 'Protest Timeline of Sentiment',
description:
- "See the sentiment towards activism of articles related to the protest",
+ 'See the sentiment towards activism of articles related to the protest',
sentimentTowards: ({ topicNode }: { topicNode: ReactNode }) => (
<>Sentiment towards {topicNode}>
),
},
},
docsPage: {
- documentation: "Documentation",
- nextPage: "Next page",
- prevPage: "Previous page",
- contents: "Contents",
- onThisPage: "On this page",
- tocAriaLabel: "Table of contents of the documentation",
- tocButtonText: "Contents",
- tocNoContentsFound: "No headings found on this page.",
+ documentation: 'Documentation',
+ nextPage: 'Next page',
+ prevPage: 'Previous page',
+ contents: 'Contents',
+ onThisPage: 'On this page',
+ tocAriaLabel: 'Table of contents of the documentation',
+ tocButtonText: 'Contents',
+ tocNoContentsFound: 'No headings found on this page.',
},
-};
+}
const textsXXX = {
- language: "XXX",
+ language: 'XXX',
seo: {
- siteTitle: "XXX",
- siteDescription: "XXX",
+ siteTitle: 'XXX',
+ siteDescription: 'XXX',
},
mainNavigation: {
- home: "XXX",
- dashboard: "XXX",
- organisations: "XXX",
- about: "XXX",
- docs: "XXX",
- fourOFour: "XXX",
- logoAssets: "XXX",
+ home: 'XXX',
+ dashboard: 'XXX',
+ organisations: 'XXX',
+ about: 'XXX',
+ docs: 'XXX',
+ fourOFour: 'XXX',
+ logoAssets: 'XXX',
themeToggle: {
- light: "XXX",
- dark: "XXX",
- system: "XXX",
- toggleText: "XXX",
+ light: 'XXX',
+ dark: 'XXX',
+ system: 'XXX',
+ toggleText: 'XXX',
},
},
fourOFour: {
- heading: "XXX",
- description: "XXX",
+ heading: 'XXX',
+ description: 'XXX',
},
errors: {
- heading: "XXX",
+ heading: 'XXX',
buttons: {
- tryAgain: "XXX",
- resetFilters: "XXX",
+ tryAgain: 'XXX',
+ resetFilters: 'XXX',
},
errorLoadingChartData: (
- { chartName = "XXX" }: { chartName?: string } = { chartName: "XXX" },
+ { chartName = 'XXX' }: { chartName?: string } = { chartName: 'XXX' },
) => `XXX ${chartName} XXX`,
apiErrorTranslations: {
defaultMessage: (
- { datasetName = "XXX" }: { datasetName?: string } = {
- datasetName: "XXX",
+ { datasetName = 'XXX' }: { datasetName?: string } = {
+ datasetName: 'XXX',
},
) => `XXX ${datasetName}. XXX.`,
ApiFetchError: (
- { datasetName = "XXX" }: { datasetName?: string } = {
- datasetName: "XXX",
+ { datasetName = 'XXX' }: { datasetName?: string } = {
+ datasetName: 'XXX',
},
) => `XXX`,
ZodError: (
- { datasetName = "XXX" }: { datasetName?: string } = {
- datasetName: "XXX",
+ { datasetName = 'XXX' }: { datasetName?: string } = {
+ datasetName: 'XXX',
},
) => `XXX ${datasetName} XXX`,
- "An error has occured: 500": (
- { datasetName = "data" }: { datasetName?: string } = {
- datasetName: "data",
+ 'An error has occured: 500': (
+ { datasetName = 'data' }: { datasetName?: string } = {
+ datasetName: 'data',
},
- ) => "XXX",
+ ) => 'XXX',
},
},
footer: {
links: {
- logoAssets: "XXX",
- gitHub: "XXX",
- appStatus: "XXX",
+ logoAssets: 'XXX',
+ gitHub: 'XXX',
+ appStatus: 'XXX',
},
- copyRightOwner: "XXX",
- hostedBy: "XXX",
- sponsoredBy: "XXX",
+ copyRightOwner: 'XXX',
+ hostedBy: 'XXX',
+ sponsoredBy: 'XXX',
},
homepage: {
hero: {
- heading: "XXX",
- text: ["XXX", "XXX"],
+ heading: 'XXX',
+ text: ['XXX', 'XXX'],
buttons: {
- goToDashboard: "XXX",
- about: "XXX",
- docs: "XXX",
+ goToDashboard: 'XXX',
+ about: 'XXX',
+ docs: 'XXX',
},
newsletterCallout: {
- heading: "XXX",
+ heading: 'XXX',
},
backgroundImage: {
- lightAlt: "XXX",
- darkAlt: "XXX",
+ lightAlt: 'XXX',
+ darkAlt: 'XXX',
},
},
},
newsLetterSection: {
- heading: "XXX",
- screenshotLightAlt: "XXX",
- screenshotDarkAlt: "XXX",
- invalidEmail: "XXX",
- inputLabel: "XXX",
- inputPlaceholder: "XXX",
- submitButton: "XXX",
+ heading: 'XXX',
+ screenshotLightAlt: 'XXX',
+ screenshotDarkAlt: 'XXX',
+ invalidEmail: 'XXX',
+ inputLabel: 'XXX',
+ inputPlaceholder: 'XXX',
+ submitButton: 'XXX',
},
uiCommon: {
- showMore: "XXX",
- showLess: "XXX",
- nextPageAriaLabel: "XXX",
- prevPageAriaLabel: "XXX",
+ showMore: 'XXX',
+ showLess: 'XXX',
+ nextPageAriaLabel: 'XXX',
+ prevPageAriaLabel: 'XXX',
},
filters: {
mediaSource: {
- label: "XXX",
- fieldPlaceholder: "XXX",
+ label: 'XXX',
+ fieldPlaceholder: 'XXX',
values: {
onlineNews: {
- name: "XXX",
- description: "XXX",
+ name: 'XXX',
+ description: 'XXX',
links: [
{
- label: "XXX",
- href: "XXX",
+ label: 'XXX',
+ href: 'XXX',
},
],
},
printNews: {
- name: "XXX",
- description: "XXX",
+ name: 'XXX',
+ description: 'XXX',
links: [
{
- label: "XXX",
- href: "XXX",
+ label: 'XXX',
+ href: 'XXX',
},
{
- label: "XXX",
- href: "XXX",
+ label: 'XXX',
+ href: 'XXX',
},
],
},
googleTrends: {
- name: "XXX",
- description: "XXX",
+ name: 'XXX',
+ description: 'XXX',
links: [
{
- label: "XXX",
- href: "XXX",
+ label: 'XXX',
+ href: 'XXX',
},
],
},
},
},
organisations: {
- label: "XXX",
- selectOrganisation: "XXX",
- noOrganisationsFound: "XXX",
- toggleAllNone: "XXX",
- unknownOrganisation: "XXX",
+ label: 'XXX',
+ selectOrganisation: 'XXX',
+ noOrganisationsFound: 'XXX',
+ toggleAllNone: 'XXX',
+ unknownOrganisation: 'XXX',
},
timeRange: {
- label: "XXX",
- presets: "XXX",
- last6MonthsLong: "XXX",
- last6MonthsShort: "XXX",
- last12MonthsLong: "XXX",
- last12MonthsShort: "XXX",
- last30DaysLong: "XXX",
- last30DaysShort: "XXX",
+ label: 'XXX',
+ presets: 'XXX',
+ last6MonthsLong: 'XXX',
+ last6MonthsShort: 'XXX',
+ last12MonthsLong: 'XXX',
+ last12MonthsShort: 'XXX',
+ last30DaysLong: 'XXX',
+ last30DaysShort: 'XXX',
buttons: {
- apply: "XXX",
- cancel: "XXX",
- resetDefaults: "XXX",
+ apply: 'XXX',
+ cancel: 'XXX',
+ resetDefaults: 'XXX',
},
},
},
info: {
welcome_message: {
- heading: "XXX",
- description: ["XXX", "XXX"],
+ heading: 'XXX',
+ description: ['XXX', 'XXX'],
buttons: {
- cta: "XXX",
- docs: "XXX",
- whatIsThis: "XXX",
+ cta: 'XXX',
+ docs: 'XXX',
+ whatIsThis: 'XXX',
},
arrowHints: {
- setYourFilters: "XXX",
- scrollDown: "XXX",
+ setYourFilters: 'XXX',
+ scrollDown: 'XXX',
},
},
},
charts: {
help: {
- howToReadThis: "XXX",
- readInTheDocs: "XXX",
+ howToReadThis: 'XXX',
+ readInTheDocs: 'XXX',
tabs: {
- info: "XXX",
- methodology: "XXX",
- data: "XXX",
+ info: 'XXX',
+ methodology: 'XXX',
+ data: 'XXX',
},
},
common: {
- legend: "XXX",
- articlesUnit: "XXX",
- total: "XXX",
- dataCredit: "XXX",
- loading: "XXX...",
+ legend: 'XXX',
+ articlesUnit: 'XXX',
+ total: 'XXX',
+ dataCredit: 'XXX',
+ loading: 'XXX...',
cantShowThisChart: {
- heading: "XXX",
- text: "XXX",
+ heading: 'XXX',
+ text: 'XXX',
limitationTranslations: {
// CAUTION: The text below is the the original message from the API
// used as s key to translate it. Only change the value on the right
// of the colon ":"
- "Sentiment trend requires fulltext analysis, which is only available for news_online, not web_google.": `XXX`,
- "Sentiment trend requires fulltext analysis, which is only available for news_online, not news_print.": `XXX`,
+ 'Sentiment trend requires fulltext analysis, which is only available for news_online, not web_google.': `XXX`,
+ 'Sentiment trend requires fulltext analysis, which is only available for news_online, not news_print.': `XXX`,
},
},
},
topics: {
- positive: "XXX",
- negative: "XXX",
- neutral: "XXX",
- "climate activism": "XXX",
- "climate crisis framing": "XXX",
- "climate policy": "XXX",
- "climate science": "XXX",
+ positive: 'XXX',
+ negative: 'XXX',
+ neutral: 'XXX',
+ 'climate activism': 'XXX',
+ 'climate crisis framing': 'XXX',
+ 'climate policy': 'XXX',
+ 'climate science': 'XXX',
},
aggregationUnit: {
- day: "XXX",
- week: "XXX",
- month: "XXX",
- year: "XXX",
+ day: 'XXX',
+ week: 'XXX',
+ month: 'XXX',
+ year: 'XXX',
},
keywordsTooltip: {
intro: ({
categoryNode,
keywordsCount,
- }: { categoryNode: ReactNode; keywordsCount: number | string }) => (
+ }: {
+ categoryNode: ReactNode
+ keywordsCount: number | string
+ }) => (
<>
XXX {categoryNode} XXX {keywordsCount} XXX
>
@@ -859,19 +872,19 @@ const textsXXX = {
},
impact: {
buttons: {
- computeImpacts: "XXX",
- hideComputedImpacts: "XXX",
+ computeImpacts: 'XXX',
+ hideComputedImpacts: 'XXX',
},
tooltips: {
- upTo: "XXX",
- downTo: "XXX",
- atLeast: "XXX",
- noImpact: "XXX",
+ upTo: 'XXX',
+ downTo: 'XXX',
+ atLeast: 'XXX',
+ noImpact: 'XXX',
},
descriptions: {
unclearChange: ({ isSentiment, topicNode }: ImpactDescriptionProps) => (
<>
- XXX XXX XXX {isSentiment && <> {topicNode} >} XXX{" "}
+ XXX XXX XXX {isSentiment && <> {topicNode} >} XXX{' '}
{topicNode} XXX
>
),
@@ -883,50 +896,53 @@ const textsXXX = {
mostBound,
}: ImpactDescriptionProps) => (
<>
- XXX {isIncreasing ? "XXX" : "XXX"} XXX{" "}
- {isSentiment && <> {topicNode} >} XXX {topicNode} XXX{" "}
+ XXX {isIncreasing ? 'XXX' : 'XXX'} XXX{' '}
+ {isSentiment && <> {topicNode} >} XXX {topicNode} XXX{' '}
{leastBound} XXX {mostBound} XXX
>
),
noChange: ({ isSentiment, topicNode }: ImpactDescriptionProps) => (
<>
- XXX XXX XXX {isSentiment && <> {topicNode} >} XXX{" "}
+ XXX XXX XXX {isSentiment && <> {topicNode} >} XXX{' '}
{topicNode} XXX
>
),
},
limitation: {
- title: "XXX",
+ title: 'XXX',
message: ({ organisationNode }: { organisationNode: ReactNode }) => (
<>XXX {organisationNode} XXX>
),
- widenYourFilters: "XXX",
+ widenYourFilters: 'XXX',
limitationTranslations: {
- "Not enough events to estimate impact.": "XXX",
+ 'Not enough events to estimate impact.': 'XXX',
},
},
error: {
- message: "XXX",
- changeYourFilters: "XXX",
+ message: 'XXX',
+ changeYourFilters: 'XXX',
},
introduction: {
message: ({
organisationNode,
selectedTimeFrameNode,
}: {
- organisationNode: ReactNode;
- selectedTimeFrameNode: ReactNode;
+ organisationNode: ReactNode
+ selectedTimeFrameNode: ReactNode
}) => (
<>
XXX {organisationNode} XXX {selectedTimeFrameNode}
>
),
selectedTimeFrame: {
- label: "XXX",
+ label: 'XXX',
tooltipMessage: ({
fromNode,
toNode,
- }: { fromNode: ReactNode; toNode: ReactNode }) => (
+ }: {
+ fromNode: ReactNode
+ toNode: ReactNode
+ }) => (
<>
XXX
{fromNode} XXX {toNode}.
>
@@ -934,7 +950,10 @@ const textsXXX = {
tooltipNotice: ({
percentageNode,
organisationNode,
- }: { percentageNode: ReactNode; organisationNode: ReactNode }) => (
+ }: {
+ percentageNode: ReactNode
+ organisationNode: ReactNode
+ }) => (
<>
XXX {percentageNode} XXX {organisationNode} XXX
>
@@ -943,31 +962,31 @@ const textsXXX = {
},
},
protest_timeline: {
- heading: "XXX",
- description: ["XXX", "XXX", "XXX"],
+ heading: 'XXX',
+ description: ['XXX', 'XXX', 'XXX'],
data_credit: [
{
- label: "XXX",
+ label: 'XXX',
links: [
{
- text: "XXX",
- url: "XXX",
+ text: 'XXX',
+ url: 'XXX',
},
],
},
],
legend: {
- size: "XXX",
+ size: 'XXX',
participants: {
- day: "XXX",
- week: "XXX",
- month: "XXX",
- year: "XXX",
+ day: 'XXX',
+ week: 'XXX',
+ month: 'XXX',
+ year: 'XXX',
},
- zeroOrUnknown: "XXX",
- color: "XXX",
- organisations: "XXX",
- other: "XXX",
+ zeroOrUnknown: 'XXX',
+ color: 'XXX',
+ organisations: 'XXX',
+ other: 'XXX',
},
tooltips: {
aggregated: ({
@@ -977,35 +996,35 @@ const textsXXX = {
participantCount,
orgsCount,
}: {
- timeUnitLabel: string;
- timeValue: string;
- protestCount: number;
- participantCount: number | undefined;
- orgsCount: number;
+ timeUnitLabel: string
+ timeValue: string
+ protestCount: number
+ participantCount: number | undefined
+ orgsCount: number
}) => (
<>
- XXX {timeValue} XXX{" "}
- {protestCount.toLocaleString("en-GB")} XXX{" "}
+ XXX {timeValue} XXX{' '}
+ {protestCount.toLocaleString('en-GB')} XXX{' '}
{participantCount && (
<>
- XXX {participantCount.toLocaleString("en-GB")} XXX
+ XXX {participantCount.toLocaleString('en-GB')} XXX
>
- )}{" "}
- XXX {orgsCount > 1 ? "XXX" : "XXX"} XXX
+ )}{' '}
+ XXX {orgsCount > 1 ? 'XXX' : 'XXX'} XXX
>
),
},
},
topics_trend: {
- heading: "XXX",
- description: ["XXX", "XXX"],
+ heading: 'XXX',
+ description: ['XXX', 'XXX'],
data_credit: [
{
- label: "XXX",
+ label: 'XXX',
links: [
{
- text: "XXX",
- url: "XXX",
+ text: 'XXX',
+ url: 'XXX',
},
],
},
@@ -1013,111 +1032,110 @@ const textsXXX = {
},
topics_impact: {
- heading: "XXX",
- description: "XXX",
+ heading: 'XXX',
+ description: 'XXX',
},
sentiment_protest: {
- heading: "XXX",
- description: ["XXX"],
+ heading: 'XXX',
+ description: ['XXX'],
data_credit: [
{
- label: "XXX",
+ label: 'XXX',
links: [
{
- text: "XXX",
- url: "XXX",
+ text: 'XXX',
+ url: 'XXX',
},
],
},
],
},
sentiment_protest_impact: {
- heading: "XXX",
- description: "XXX",
+ heading: 'XXX',
+ description: 'XXX',
},
sentiment_policy: {
- heading: "XXX",
- description: ["XXX"],
+ heading: 'XXX',
+ description: ['XXX'],
data_credit: [
{
- label: "XXX",
+ label: 'XXX',
links: [
{
- text: "XXX",
- url: "XXX",
+ text: 'XXX',
+ url: 'XXX',
},
],
},
],
},
sentiment_policy_impact: {
- heading: "XXX",
- description: "XXX",
+ heading: 'XXX',
+ description: 'XXX',
},
},
organisationsPage: {
- heading: "XXX",
+ heading: 'XXX',
description: (props: {
- isSameDay: boolean;
- formattedFrom: string;
- formattedTo: string;
- orgsCount: number;
+ isSameDay: boolean
+ formattedFrom: string
+ formattedTo: string
+ orgsCount: number
}) => {
- return `XXX`;
+ return `XXX`
},
- allOrganisations: "XXX",
- showPartnersAriaLabel: "XXX",
+ allOrganisations: 'XXX',
+ showPartnersAriaLabel: 'XXX',
propertyNames: {
- name: "XXX",
- totalEvents: "XXX",
- totalParticipants: "XXX",
- avgParticipants: "XXX",
- avgPartners: "XXX",
- totalPartners: "XXX",
+ name: 'XXX',
+ totalEvents: 'XXX',
+ totalParticipants: 'XXX',
+ avgParticipants: 'XXX',
+ totalPartners: 'XXX',
},
},
singleProtestPage: {
heading: (props: {
- formattedDate: string;
- orgsCount: number;
- orgName: string;
+ formattedDate: string
+ orgsCount: number
+ orgName: string
}) => `XXX`,
propertyNames: {
- city: "XXX",
- country: "XXX",
- organisations: "XXX",
+ city: 'XXX',
+ country: 'XXX',
+ organisations: 'XXX',
},
table: {
- heading: "XXX",
- description: "XXX",
+ heading: 'XXX',
+ description: 'XXX',
header: {
- title: "XXX",
- summary: "XXX",
- date: "XXX",
- url: "XXX",
- sentimentActivism: "XXX",
- sentimentPolicy: "XXX",
+ title: 'XXX',
+ summary: 'XXX',
+ date: 'XXX',
+ url: 'XXX',
+ sentimentActivism: 'XXX',
+ sentimentPolicy: 'XXX',
},
},
charts: {
- heading: "XXX",
- description: "XXX",
+ heading: 'XXX',
+ description: 'XXX',
sentimentTowards: ({ topicNode }: { topicNode: ReactNode }) => (
<>XXX {topicNode}>
),
},
},
docsPage: {
- documentation: "XXX",
- nextPage: "XXX",
- prevPage: "XXX",
- contents: "XXX",
- onThisPage: "XXX",
- tocAriaLabel: "XXX",
- tocButtonText: "XXX",
- tocNoContentsFound: "XXX",
+ documentation: 'XXX',
+ nextPage: 'XXX',
+ prevPage: 'XXX',
+ contents: 'XXX',
+ onThisPage: 'XXX',
+ tocAriaLabel: 'XXX',
+ tocButtonText: 'XXX',
+ tocNoContentsFound: 'XXX',
},
-} satisfies typeof textsEnGB;
+} satisfies typeof textsEnGB
-export const texts = textsEnGB;
+export const texts = textsEnGB
// export const texts = textsXXX;
diff --git a/frontend-nextjs/src/utility/useEventMedia.ts b/frontend-nextjs/src/utility/useEventMedia.ts
index 794d6ba..e257076 100644
--- a/frontend-nextjs/src/utility/useEventMedia.ts
+++ b/frontend-nextjs/src/utility/useEventMedia.ts
@@ -9,11 +9,12 @@ import {
import type { OrganisationType, ParsedEventType } from "./eventsUtil";
import { getStaleTime } from "./queryUtil";
import { today } from "./today";
-import useEvents from "./useEvents";
+import { useAllOrganisations } from "./useOrganisations";
import useQueryErrorToast from "./useQueryErrorToast";
-export function getEventMediaQueryOptions(
+function getEventMediaQueryOptions(
allOrganisations: OrganisationType[],
+ isLoading: boolean,
query?: Partial,
) {
const queryParsing = eventMediaInputQueryZodSchema.safeParse(query);
@@ -34,18 +35,18 @@ export function getEventMediaQueryOptions(
)
: null,
staleTime: getStaleTime(today),
- enabled: queryParsing.success,
+ enabled: !isLoading && queryParsing.success,
});
}
function useEventMedia(eventId?: ParsedEventType["event_id"]) {
- const { data } = useEvents();
+ const { organisations, isLoading } = useAllOrganisations();
const organizers = useFiltersStore(({ organizers }) => organizers);
const mediaSource = useFiltersStore(({ mediaSource }) => mediaSource);
const from = useFiltersStore(({ from }) => from);
const to = useFiltersStore(({ to }) => to);
const query = useQuery(
- getEventMediaQueryOptions(data?.organisations || [], {
+ getEventMediaQueryOptions(organisations || [], isLoading, {
eventId,
organizers,
from,
diff --git a/frontend-nextjs/src/utility/useEvents.ts b/frontend-nextjs/src/utility/useEvents.ts
index 1855366..f65caf9 100644
--- a/frontend-nextjs/src/utility/useEvents.ts
+++ b/frontend-nextjs/src/utility/useEvents.ts
@@ -1,114 +1,109 @@
-"use client";
-import { useFiltersStore } from "@/providers/FiltersStoreProvider";
-import { useToday } from "@/providers/TodayProvider";
+'use client'
+import { useFiltersStore } from '@/providers/FiltersStoreProvider'
+import { useToday } from '@/providers/TodayProvider'
import {
type UseQueryResult,
useQuery,
useQueryClient,
-} from "@tanstack/react-query";
-import { getTime } from "date-fns";
-import { useEffect, useMemo } from "react";
+} from '@tanstack/react-query'
+import { getTime } from 'date-fns'
+import { useEffect } from 'react'
import {
type OrganisationType,
type ParsedEventType,
- extractEventOrganisations,
- getEventsData,
-} from "./eventsUtil";
-import { getStaleTime } from "./queryUtil";
-import useQueryErrorToast from "./useQueryErrorToast";
+ getEventsData
+} from './eventsUtil'
+import { getStaleTime } from './queryUtil'
+import { useOrganizersKey } from './useOrganisations'
+import useQueryErrorToast from './useQueryErrorToast'
export type UseEventsReturnType = Omit<
UseQueryResult,
- "data"
+ 'data'
> & {
data: {
- allEvents: ParsedEventType[];
- events: ParsedEventType[];
- eventsByOrgs: ParsedEventType[];
- organisations: OrganisationType[];
- selectedOrganisations: OrganisationType[];
- };
-};
+ allEvents: ParsedEventType[]
+ events: ParsedEventType[]
+ eventsByOrgs: ParsedEventType[]
+ organisations: OrganisationType[]
+ selectedOrganisations: OrganisationType[]
+ }
+}
+
+export function useAllEvents() {
+ const queryClient = useQueryClient()
+ const { today } = useToday()
-function useEvents(): UseEventsReturnType {
- const queryClient = useQueryClient();
- const { today } = useToday();
- const fromTime = useFiltersStore(({ from }) => getTime(from));
- const toTime = useFiltersStore(({ to }) => getTime(to));
- const fromDateString = useFiltersStore(({ fromDateString }) => fromDateString);
- const toDateString = useFiltersStore(({ toDateString }) => toDateString);
- const organizers = useFiltersStore(({ organizers }) => organizers);
- const queryKey = ["events"];
const query = useQuery({
- queryKey,
+ queryKey: ['allEvents'],
queryFn: async () => await getEventsData(undefined, today),
staleTime: getStaleTime(today),
- });
- const { data, error } = query;
-
- useQueryErrorToast("protests", error);
-
- const organisersKey = useMemo(
- () => organizers.sort().join("-"),
- [organizers],
- );
- const events = useMemo(() => {
- return (data ?? []).filter((e) => {
- if (!e.date) return false;
- const beforeFrom = e.time < fromTime;
- const afterTo = e.time > toTime;
- if (beforeFrom || afterTo) return false;
- return true;
- });
- // eslint-disable-next-line react-hooks/exhaustive-deps
- }, [data, fromDateString, toDateString]);
-
- const eventFilteredByOrganizers = useMemo(() => {
- if (organizers.length === 0) return events;
- return events.filter((e) =>
- organizers.find((orgSlug) =>
- e.organizers.find((x) => x.slug === orgSlug),
- ),
- );
- // eslint-disable-next-line react-hooks/exhaustive-deps
- }, [events, organisersKey]);
-
- const organisations = useMemo(
- () => (data?.length ? extractEventOrganisations(data) : []),
- [data],
- );
+ })
+ const { data, error } = query
- const orgsFromFilteredEvents = useMemo(
- () => (events.length ? extractEventOrganisations(events) : []),
- [events],
- );
-
- const selectedOrganisations = useMemo(() => {
- if (organizers.length === 0) return orgsFromFilteredEvents;
- const selectedOrs = orgsFromFilteredEvents.filter((org) =>
- organizers.find((o) => o === org.slug),
- );
- return selectedOrs.length === 0 ? orgsFromFilteredEvents : selectedOrs;
- }, [organizers, orgsFromFilteredEvents]);
+ useQueryErrorToast('protests', error)
useEffect(() => {
- if (!data || data.length === 0) return;
+ if (!data || data.length === 0) return
for (const event of data) {
- queryClient.setQueryData(["events", event.event_id], event);
+ queryClient.setQueryData(['events', event.event_id], event)
}
- // eslint-disable-next-line react-hooks/exhaustive-deps
- }, [data]);
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [data])
return {
...query,
- data: {
- allEvents: data ?? [],
- events,
- eventsByOrgs: eventFilteredByOrganizers,
- organisations,
- selectedOrganisations,
+ allEvents: data ?? [],
+ }
+}
+
+export function useTimeFilteredEvents() {
+ const { allEvents, isLoading } = useAllEvents()
+ const fromTime = useFiltersStore(({ from }) => getTime(from))
+ const toTime = useFiltersStore(({ to }) => getTime(to))
+
+ const query = useQuery({
+ queryKey: ['timeFilteredEvents', fromTime, toTime],
+ queryFn: () => {
+ const eventsInTimeRange = (allEvents ?? []).filter((e) => {
+ if (!e.date) return false
+ const beforeFrom = e.time < fromTime
+ const afterTo = e.time > toTime
+ if (beforeFrom || afterTo) return false
+ return true
+ })
+ return eventsInTimeRange
},
- };
+ enabled: !isLoading && allEvents.length > 0,
+ })
+
+ return {
+ ...query,
+ timeFilteredEvents: query.data ?? [],
+ }
}
-export default useEvents;
+export function useFilteredEvents() {
+ const { timeFilteredEvents, isLoading } = useTimeFilteredEvents()
+ const organizers = useFiltersStore(({ organizers }) => organizers)
+ const organizersKey = useOrganizersKey()
+
+ const query = useQuery({
+ queryKey: ['filteredEvents', timeFilteredEvents, organizersKey],
+ queryFn: () => {
+ if (organizers.length === 0) return timeFilteredEvents
+ const eventsOfSelectedOrgs = timeFilteredEvents.filter((e) =>
+ organizers.find((orgSlug) =>
+ e.organizers.find((x) => x.slug === orgSlug),
+ ),
+ );
+ return eventsOfSelectedOrgs
+ },
+ enabled: !isLoading
+ })
+
+ return {
+ ...query,
+ filteredEvents: query.data ?? [],
+ }
+}
diff --git a/frontend-nextjs/src/utility/useFullTexts.ts b/frontend-nextjs/src/utility/useFullTexts.ts
index 7292edf..7023778 100644
--- a/frontend-nextjs/src/utility/useFullTexts.ts
+++ b/frontend-nextjs/src/utility/useFullTexts.ts
@@ -1,14 +1,14 @@
import { useFiltersStore } from "@/providers/FiltersStoreProvider";
import { useToday } from "@/providers/TodayProvider";
+import { useAllOrganisations } from '@/utility/useOrganisations';
import { useQuery } from "@tanstack/react-query";
import { useMemo } from "react";
import slugify from "slugify";
import { getFullTextsData } from "./fullTextsUtil";
-import useEvents from "./useEvents";
export function useFullTexts({ event_id }: { event_id?: string }) {
const organizers = useFiltersStore(({ organizers }) => organizers);
- const { data } = useEvents();
+ const { organisations, isLoading } = useAllOrganisations();
const { today } = useToday();
const organizersKey = useMemo(
() =>
@@ -25,10 +25,10 @@ export function useFullTexts({ event_id }: { event_id?: string }) {
getFullTextsData({
event_id: event_id || "",
organizers,
- allOrganisations: data.organisations || [],
+ allOrganisations: organisations || [],
today,
}),
- enabled: !!event_id,
+ enabled: !!event_id && !isLoading,
});
return query;
}
diff --git a/frontend-nextjs/src/utility/useMediaImpact.ts b/frontend-nextjs/src/utility/useMediaImpact.ts
index 304e548..c5c572d 100644
--- a/frontend-nextjs/src/utility/useMediaImpact.ts
+++ b/frontend-nextjs/src/utility/useMediaImpact.ts
@@ -6,7 +6,7 @@ import { getMediaImpactData } from "./mediaImpactUtil";
import type { TrendQueryProps } from "./mediaTrendUtil";
import { getStaleTime } from "./queryUtil";
import { today } from "./today";
-import useEvents from "./useEvents";
+import { useAllOrganisations } from "./useOrganisations";
import useQueryErrorToast from "./useQueryErrorToast";
function useMediaImpactData({
@@ -32,7 +32,7 @@ function useMediaImpactData({
toDateString,
mediaSource,
];
- const { data } = useEvents();
+ const { organisations, isLoading } = useAllOrganisations();
const query = useQuery({
queryKey,
queryFn: async () => {
@@ -46,11 +46,11 @@ function useMediaImpactData({
organizer,
mediaSource,
},
- allOrganisations: data?.organisations || [],
+ allOrganisations: organisations || [],
});
},
staleTime: getStaleTime(today),
- enabled: organizer !== undefined && data?.organisations?.length > 0,
+ enabled: organizer !== undefined && organisations?.length > 0 && !isLoading,
});
useQueryErrorToast(`media ${trend_type} impact`, query.error);
diff --git a/frontend-nextjs/src/utility/useMediaTrends.ts b/frontend-nextjs/src/utility/useMediaTrends.ts
index 65fa3bd..f8b728e 100644
--- a/frontend-nextjs/src/utility/useMediaTrends.ts
+++ b/frontend-nextjs/src/utility/useMediaTrends.ts
@@ -5,7 +5,7 @@ import { useQuery } from "@tanstack/react-query";
import slugify from "slugify";
import { type TrendQueryProps, getMediaTrendData } from "./mediaTrendUtil";
import { getStaleTime } from "./queryUtil";
-import useEvents from "./useEvents";
+import { useAllOrganisations } from "./useOrganisations";
import useQueryErrorToast from "./useQueryErrorToast";
function useMediaTrends({
@@ -28,7 +28,7 @@ function useMediaTrends({
.sort()
.join("-"),
);
- const { data } = useEvents();
+ const { organisations, isLoading } = useAllOrganisations();
const { today } = useToday();
const queryKey = [
"mediaTrends",
@@ -52,12 +52,12 @@ function useMediaTrends({
organizers,
mediaSource,
},
- allOrganisations: data.organisations || [],
+ allOrganisations: organisations || [],
},
today,
),
staleTime: getStaleTime(today),
- enabled,
+ enabled: enabled && !isLoading,
});
useQueryErrorToast(`media ${trend_type} trends`, query.error);
diff --git a/frontend-nextjs/src/utility/useOrganisations.ts b/frontend-nextjs/src/utility/useOrganisations.ts
index b185107..326cc66 100644
--- a/frontend-nextjs/src/utility/useOrganisations.ts
+++ b/frontend-nextjs/src/utility/useOrganisations.ts
@@ -1,22 +1,92 @@
-import type { EventOrganizerSlugType } from "./eventsUtil";
-import useEvents from "./useEvents";
+import { useFiltersStore } from "@/providers/FiltersStoreProvider";
+import { useQuery } from "@tanstack/react-query";
+import { useMemo } from "react";
+import { extractEventOrganisations, type EventOrganizerSlugType } from "./eventsUtil";
+import { useAllEvents, useFilteredEvents } from "./useEvents";
-export function useOrganisations() {
- const { data, isPending } = useEvents();
+export function useAllOrganisations() {
+ const { allEvents, isLoading } = useAllEvents();
+
+ const query = useQuery({
+ queryKey: ["organisations"],
+ queryFn: () => {
+ if (!allEvents || allEvents.length === 0) return [];
+ return extractEventOrganisations(allEvents);
+ },
+ enabled: !isLoading && allEvents.length > 0,
+ });
return {
- organisations: data?.organisations ?? [],
- isPending,
- };
+ ...query,
+ organisations: query.data ?? [],
+ }
+}
+
+export function useFilteredEventsOrganisations() {
+ const { filteredEvents, isLoading } = useFilteredEvents();
+ const organizersKey = useOrganizersKey();
+ const fromDateString = useFiltersStore(({ fromDateString }) => fromDateString);
+ const toDateString = useFiltersStore(({ toDateString }) => toDateString);
+ const organizers = useFiltersStore(({ organizers }) => organizers);
+
+ const query = useQuery({
+ queryKey: ["filteredEventsOrganisations", organizersKey, fromDateString, toDateString],
+ queryFn: () => {
+ if (!filteredEvents || filteredEvents.length === 0) return [];
+ const events = organizers.length === 0 ? filteredEvents : filteredEvents.filter(
+ (event) => event.organizers.find((org) => organizers.includes(org.slug))
+ )
+ return extractEventOrganisations(events)
+ },
+ enabled: !isLoading && filteredEvents.length > 0,
+ })
+
+ return {
+ ...query,
+ filteredEventsOrganisations: query.data ?? [],
+ }
+}
+
+export function useOrganizersKey() {
+ const organizers = useFiltersStore(({ organizers }) => organizers);
+ const organisersKey = useMemo(() => organizers.sort().join('-'), [organizers])
+ return organisersKey
+}
+
+export function useSelectedOrganisations() {
+ const { organisations, isLoading } = useAllOrganisations();
+ const organizers = useFiltersStore(({ organizers }) => organizers);
+ const organizersKey = useOrganizersKey();
+
+ const query = useQuery({
+ queryKey: ["selectedOrganisations", organizersKey],
+ queryFn: () => {
+ if (organizers.length === 0) return organisations;
+ const selectedOrs = organisations.filter((org) =>
+ organizers.find((o) => o === org.slug),
+ );
+ return selectedOrs.length === 0 ? organisations : selectedOrs;
+ },
+ enabled: !isLoading && organisations.length > 0,
+ })
+
+ return {
+ ...query,
+ selectedOrganisations: query.data ?? [],
+ }
}
export function useOrganisation(slug: EventOrganizerSlugType | undefined) {
- const { organisations, isPending } = useOrganisations();
+ const { organisations, isLoading } = useAllOrganisations();
+
+ const query = useQuery({
+ queryKey: ["organisations", slug],
+ queryFn: () => slug ? organisations.find(({ slug: s }) => s === slug) : undefined,
+ enabled: !!slug && !isLoading && organisations.length > 0,
+ });
return {
- organisation: slug
- ? organisations.find(({ slug: s }) => s === slug)
- : undefined,
- isPending,
+ ...query,
+ organisation: query.data,
};
}