diff --git a/next/components/atoms/ObservationLevelTable.js b/next/components/atoms/ObservationLevelTable.js index f1f04f21..8d2bda79 100644 --- a/next/components/atoms/ObservationLevelTable.js +++ b/next/components/atoms/ObservationLevelTable.js @@ -18,16 +18,25 @@ export default function ObservationLevel({ resource }) { const headers = [t('observationLevelTable.entityHeader'), t('observationLevelTable.columnsHeader')]; - let array = [] - const keys = Object.keys(resource?.observationLevels) + function sortElements(a, b) { + if (a.order < b.order) { + return -1; + } + if (a.order > b.order) { + return 1; + } + return 0; + } - keys.forEach((elm) => { - const value = resource?.observationLevels[elm] + let array = []; + const keys = Object.keys(resource?.observationLevels); + const sortedLevels = Object.values(resource?.observationLevels).sort(sortElements); + sortedLevels.forEach((value) => { const valueEntity = () => { - if(value.entity[`name${capitalize(locale)}`]) return value.entity[`name${capitalize(locale)}`] - if(value.entity.name) return value.entity.name - return t('observationLevelTable.notProvided') + if(value.entity[`name${capitalize(locale)}`]) return value.entity[`name${capitalize(locale)}`]; + if(value.entity.name) return value.entity.name; + return t('observationLevelTable.notProvided'); } const valueColumns = () => { diff --git a/next/components/molecules/DataInformationQuery.js b/next/components/molecules/DataInformationQuery.js index 84ac9326..67ed4ffe 100644 --- a/next/components/molecules/DataInformationQuery.js +++ b/next/components/molecules/DataInformationQuery.js @@ -46,6 +46,7 @@ import CheckIcon from "../../public/img/icons/checkIcon"; import Link from "../atoms/Link"; export function CodeHighlight({ language, children }) { + const { t } = useTranslation('dataset'); const textRef = useRef(null) const [isOverflowing, setIsOverflowing] = useState(false) const [isExpanded, setIsExpanded] = useState(true) @@ -117,17 +118,17 @@ export function CodeHighlight({ language, children }) { color:"#9D9FA3", }} > - {hasCopied ? "Copiado" : "Copiar"} + {hasCopied ? t('table.copied') : t('table.copy')} {hasCopied ? : - {isExpanded ? "Recolher" : "Expandir"} + {isExpanded ? t('table.collapse') : t('table.expand')} )} diff --git a/next/components/molecules/Footer.js b/next/components/molecules/Footer.js index 97957372..e04142d4 100644 --- a/next/components/molecules/Footer.js +++ b/next/components/molecules/Footer.js @@ -93,25 +93,24 @@ export default function Footer({ template, ocult = false }) { if(template === "simple") return ( diff --git a/next/components/molecules/TableColumns.js b/next/components/molecules/TableColumns.js index cc522e2e..89ff0d71 100644 --- a/next/components/molecules/TableColumns.js +++ b/next/components/molecules/TableColumns.js @@ -668,8 +668,8 @@ export default function TableColumns({ - {elm?.node?.coverage?.start && elm?.node?.coverage?.end ? - elm.node.coverage.start +" - "+ elm.node.coverage.end + {elm?.node?.temporalCoverage?.start && elm?.node?.temporalCoverage?.end ? + elm.node.temporalCoverage.start +" - "+ elm.node.temporalCoverage.end : t('column.notProvided') } diff --git a/next/components/molecules/ThemeCatalog.js b/next/components/molecules/ThemeCatalog.js index d5830062..42165eec 100644 --- a/next/components/molecules/ThemeCatalog.js +++ b/next/components/molecules/ThemeCatalog.js @@ -18,7 +18,7 @@ import { triggerGAEvent } from "../../utils"; import Carousel from "../atoms/Carousel"; import SectionText from "../atoms/SectionText"; -import DatasetCard from "../organisms/DatasetCard"; +import DatasetThemeCatalogCard from "../organisms/DatasetThemeCatalogCard"; import RemoveIcon from "../../public/img/icons/removeIcon"; import styles from "../../styles/themeCatalog.module.css"; @@ -249,11 +249,11 @@ function CardThemes ({ responsive, datasetsCards = [], loading, locale }) { )) : datasetsCards.map((elm, i) => ( - { @@ -55,7 +55,6 @@ export default function DatasetResource({ } useEffect(() => { - let dataset_tables = dataset?.tables?.edges.map((elm) => elm.node) .filter((elm) => elm?.status?.slug !== "under_review") .filter((elm) => elm?.slug !== "dicionario") @@ -194,10 +193,24 @@ export default function DatasetResource({ ) } - function SelectResource() { - const [widthScreen, setWidthScreen] = useState(0) + function SelectResource ({ selectedResource }) { + const { t } = useTranslation('dataset'); + const [widthScreen, setWidthScreen] = useState(0); + const [value, setValue] = useState("") + const {table, raw_data_source, information_request} = selectedResource; + + const findResourceName = (source, id) => { + const resource = source.find(item => item._id === id); + return resource ? resource[`name${capitalize(locale)}`] || resource.name : ""; + }; useEffect(() => { + setValue( + table ? findResourceName(tables, table) : + raw_data_source ? findResourceName(rawDataSources, raw_data_source) : + information_request ? findResourceName(informationRequests, information_request) : "" + ); + const updateWidthScreen = () => { setWidthScreen(window.innerWidth - 48) } @@ -209,35 +222,49 @@ export default function DatasetResource({ return () => { window.removeEventListener('resize', updateWidthScreen) } - }, []) + }, [table, raw_data_source, information_request]); return ( - - - Selecione uma tabela ou fonte original - - - + + {value} + + + + : - + } diff --git a/next/components/organisms/Dataset.js b/next/components/organisms/DatasetSearchCard.js similarity index 84% rename from next/components/organisms/Dataset.js rename to next/components/organisms/DatasetSearchCard.js index e84fd8c6..7acd9796 100644 --- a/next/components/organisms/Dataset.js +++ b/next/components/organisms/DatasetSearchCard.js @@ -15,12 +15,15 @@ import Link from '../atoms/Link'; import LinkIcon from "../../public/img/icons/linkIcon"; import InfoArrowIcon from "../../public/img/icons/infoArrowIcon"; import { DataBaseSolidIcon } from "../../public/img/icons/databaseIcon"; +import axios from "axios"; +import { useState, useEffect } from "react"; export default function Dataset({ id, name, + organizations, temporalCoverageText, - organization, + spatialCoverage, tables, rawDataSources, informationRequests, @@ -28,7 +31,7 @@ export default function Dataset({ locale }) { const { t } = useTranslation('dataset'); - const router = useRouter(); + const allowedURLs = ["https://basedosdados.org", "https://staging.basedosdados.org"] const Tables = () => { let tablesNumber = tables.number @@ -155,8 +158,10 @@ export default function Dataset({ _hover={{ opacity: 0.9 }} > {organization[`name${capitalize(locale)}`] - {organization[`name${capitalize(locale)}`] || organization?.name} + {organizations[0]?.[`name${capitalize(locale)}`] || organizations[0]?.name} @@ -261,10 +266,36 @@ export default function Dataset({ lineHeight="20px" color="#71757A" > - {temporalCoverageText ? temporalCoverageText : t('noCoverage')} + {temporalCoverageText ? temporalCoverageText : t('notProvided')} + {!allowedURLs.includes(process.env.NEXT_PUBLIC_BASE_URL_FRONTEND) && + + + {t('spatialCoverage')}: + + + {spatialCoverage ? spatialCoverage : t('notProvided')} + + + } + - + - {organization[`name${capitalize(locale)}`] || organization.name || organization.slug} + {organizations?.[0]?.[`name${capitalize(locale)}`] || organizations?.[0]?.name || organizations?.[0]?.slug} diff --git a/next/components/organisms/TablePage.js b/next/components/organisms/TablePage.js index 82895657..642898ed 100644 --- a/next/components/organisms/TablePage.js +++ b/next/components/organisms/TablePage.js @@ -36,16 +36,17 @@ export default function TablePage({ id }) { const [resource, setResource] = useState({}) const [isError, setIsError] = useState(false) + const allowedURLs = ["https://basedosdados.org", "https://staging.basedosdados.org"] + useEffect(() => { const fetchData = async () => { - setIsLoading(true) try { const response = await fetch(`/api/tables/getTable?id=${id}&locale=${locale}`, { method: "GET" }) const result = await response.json() if (result.success) { - setResource(result.resource) - setIsError(false) + setResource(result.resource); + setIsError(false); } else { console.error(result.error) setIsError(true) @@ -58,7 +59,8 @@ export default function TablePage({ id }) { } } - fetchData() + setIsLoading(true); + fetchData(); }, [id, locale]) const TooltipText = ({ text, info, ...props }) => { @@ -144,38 +146,49 @@ export default function TablePage({ id }) { } const PublishedOrDataCleanedBy = ({ resource }) => { + if (!resource || typeof resource !== 'object' || Object.keys(resource).length === 0) { + return ( + + {t('table.notProvided')} + + ); + } + + const people = Object.values(resource); + return ( - - {resource?.firstName && resource?.lastName ? - - {`${resource.firstName} ${resource.lastName}`} - - : - - {t('table.notProvided')} - - } - {resource?.email && } - {resource?.github && } - {resource?.website && } - {resource?.twitter && } - - ) - } + + {people.map((person, index) => ( + + + {person?.firstName && person?.lastName + ? `${person.firstName} ${person.lastName}` + : t('table.notProvided') + } + + {person?.email && } + {person?.github && } + {person?.website && } + {person?.twitter && } + + ))} + + ); + }; const StackSkeleton = ({ children, ...props }) => { return ( @@ -232,8 +245,8 @@ export default function TablePage({ id }) { return formats[value] ? formats[value] : t('table.updateNotDefined') } - if(isError) return - + if (isError) return ; + return ( - + + {!allowedURLs.includes(process.env.NEXT_PUBLIC_BASE_URL_FRONTEND) && + + + + {t('table.spatialCoverage')} + + + + + + {resource?.[`spatialCoverageName${capitalize(locale)}`] + ? Object.values(resource[`spatialCoverageName${capitalize(locale)}`]) + .sort((a, b) => a.localeCompare(b, locale)) + .join(', ') + : t('table.notProvided')} + + + + } + {resource?.rawDataSource?.[0]?.updates?.[0]?.latest ? - `${formatDate(resource.rawDataSource[0].updates[0].latest)}:` + `${formatDate(resource.rawDataSource[0].updates[0].latest)}` : t('table.notProvided') }: {t('table.lastUpdateRawDataSource')} @@ -471,7 +519,7 @@ export default function TablePage({ id }) { color="#464A51" > {resource?.rawDataSource?.[0]?.polls?.[0]?.latest ? - `${formatDate(resource.rawDataSource[0].polls[0].latest)}:` + `${formatDate(resource.rawDataSource[0].polls[0].latest)}` : t('table.notProvided') }: {t('table.lastCheckRawDataSource')} diff --git a/next/pages/api/areas/getArea.js b/next/pages/api/areas/getArea.js new file mode 100644 index 00000000..05874a93 --- /dev/null +++ b/next/pages/api/areas/getArea.js @@ -0,0 +1,45 @@ +import axios from "axios"; +import { capitalize } from 'lodash'; + +const API_URL = `${process.env.NEXT_PUBLIC_API_URL}/api/v1/graphql`; + +async function getArea(slug, locale) { + try { + const res = await axios({ + url: API_URL, + method: "POST", + data: { + query: ` + query { + allArea (slug: "${slug}") { + edges { + node { + _id + slug + name + name${capitalize(locale)} + } + } + } + } + `, + variables: null + } + }); + const data = res?.data?.data?.allArea?.edges; + return data; + } catch (error) { + console.error(error); + return null; + } +} + +export default async function handler(req, res) { + const { slug, locale = 'pt' } = req.query; + const result = await getArea(slug, locale); + + if(result.errors) return res.status(500).json({error: result.errors, success: false}) + if(result === "err") return res.status(500).json({error: "err", success: false}) + + return res.status(200).json({resource: result, success: true}) +} \ No newline at end of file diff --git a/next/pages/api/areas/getAreas.js b/next/pages/api/areas/getAreas.js new file mode 100644 index 00000000..0e7ddfea --- /dev/null +++ b/next/pages/api/areas/getAreas.js @@ -0,0 +1,45 @@ +import axios from "axios"; +import { capitalize } from 'lodash'; + +const API_URL = `${process.env.NEXT_PUBLIC_API_URL}/api/v1/graphql`; + +async function getAreas(locale = 'pt') { + try { + const res = await axios({ + url: API_URL, + method: "POST", + data: { + query: ` + query { + allArea { + edges { + node { + _id + slug + name + name${capitalize(locale)} + } + } + } + } + `, + variables: null + } + }); + const data = res?.data?.data?.allArea?.edges; + return data; + } catch (error) { + console.error(error); + return null; + } +} + +export default async function handler(req, res) { + const { locale } = req.query; + const result = await getAreas(locale); + + if(result.errors) return res.status(500).json({error: result.errors, success: false}) + if(result === "err") return res.status(500).json({error: "err", success: false}) + + return res.status(200).json({resource: result, success: true}) +} \ No newline at end of file diff --git a/next/pages/api/areas/getCountries.js b/next/pages/api/areas/getCountries.js new file mode 100644 index 00000000..22b38ea3 --- /dev/null +++ b/next/pages/api/areas/getCountries.js @@ -0,0 +1,46 @@ +import axios from "axios"; +import { capitalize } from 'lodash'; + +const API_URL = `${process.env.NEXT_PUBLIC_API_URL}/api/v1/graphql`; + +async function getAreas(locale = 'pt') { + try { + const res = await axios({ + url: API_URL, + method: "POST", + data: { + query: ` + query { + allArea (administrativeLevel: A_0) { + edges { + node { + _id + slug + name + name${capitalize(locale)} + } + } + } + } + `, + variables: null + } + }); + const data = res?.data?.data?.allArea?.edges; + + return data; + } catch (error) { + console.error(error); + return null; + } +} + +export default async function handler(req, res) { + const { locale } = req.query; + const result = await getAreas(locale); + + if(result.errors) return res.status(500).json({error: result.errors, success: false}) + if(result === "err") return res.status(500).json({error: "err", success: false}) + + return res.status(200).json({resource: result, success: true}) +} \ No newline at end of file diff --git a/next/pages/api/datasets/getDataset.js b/next/pages/api/datasets/getDataset.js index 60e73e92..482ca97a 100644 --- a/next/pages/api/datasets/getDataset.js +++ b/next/pages/api/datasets/getDataset.js @@ -20,7 +20,9 @@ export default async function getDataset(id, locale = 'pt') { name${capitalize(locale)} description description${capitalize(locale)} - coverage + temporalCoverage + spatialCoverage + spatialCoverageName${capitalize(locale)} themes { edges { node { @@ -39,13 +41,17 @@ export default async function getDataset(id, locale = 'pt') { } } } - organization { - _id - slug - name - name${capitalize(locale)} - website - picture + organizations { + edges { + node { + _id + slug + name + name${capitalize(locale)} + website + picture + } + } } informationRequests { edges { diff --git a/next/pages/api/rawDataSources/getRawDataSource.js b/next/pages/api/rawDataSources/getRawDataSource.js index cadc8bdb..4b9fc868 100644 --- a/next/pages/api/rawDataSources/getRawDataSource.js +++ b/next/pages/api/rawDataSources/getRawDataSource.js @@ -61,6 +61,7 @@ async function getRawDataSource(id, locale = 'pt') { edges { node { _id + order columns { edges { node { diff --git a/next/pages/api/tables/getTable.js b/next/pages/api/tables/getTable.js index a921390d..e012a7a1 100644 --- a/next/pages/api/tables/getTable.js +++ b/next/pages/api/tables/getTable.js @@ -25,14 +25,14 @@ async function getTable(id, locale='pt') { dataset { _id slug - organization { - _id - slug - name - name${capitalize(locale)} - area { - _id - slug + organizations { + edges { + node { + _id + slug + name + name${capitalize(locale)} + } } } } @@ -46,7 +46,15 @@ async function getTable(id, locale='pt') { } } version - fullCoverage + status { + _id + slug + } + isDeprecated + temporalCoverage + spatialCoverage + spatialCoverageName${capitalize(locale)} + fullTemporalCoverage rawDataSource { edges { node { @@ -105,6 +113,7 @@ async function getTable(id, locale='pt') { edges { node { _id + order columns { edges { node { diff --git a/next/pages/api/tables/getTableColumns.js b/next/pages/api/tables/getTableColumns.js index 1c3f83fb..39b076b5 100644 --- a/next/pages/api/tables/getTableColumns.js +++ b/next/pages/api/tables/getTableColumns.js @@ -55,7 +55,9 @@ async function getTableColumns(id, locale = 'pt') { bigqueryType { name } - coverage + temporalCoverage + spatialCoverage + spatialCoverageName${capitalize(locale)} measurementUnit containsSensitiveData observations diff --git a/next/pages/dataset/[dataset].js b/next/pages/dataset/[dataset].js index f1019d18..223dffa5 100644 --- a/next/pages/dataset/[dataset].js +++ b/next/pages/dataset/[dataset].js @@ -15,6 +15,7 @@ import Head from "next/head"; import { useTranslation } from 'next-i18next'; import { serverSideTranslations } from 'next-i18next/serverSideTranslations'; import { capitalize } from 'lodash'; +import axios from "axios"; import BigTitle from "../../components/atoms/BigTitle"; import GreenTab from "../../components/atoms/GreenTab"; @@ -35,17 +36,21 @@ import { export async function getStaticProps(context) { const { locale, params } = context; let dataset = null; + try { dataset = await getDataset(params.dataset, locale || 'pt'); + } catch (error) { console.error("Fetch error:", error.message); } + const props = { + ...(await serverSideTranslations(locale, ['dataset', 'common', 'menu', 'prices'])), + dataset, + }; + return { - props: { - ...(await serverSideTranslations(locale, ['dataset', 'common', 'menu', 'prices'])), - dataset, - }, + props, revalidate: 30, }; } @@ -68,12 +73,10 @@ export default function DatasetPage ({ dataset }) { const { query } = router const [tabIndex, setTabIndex] = useState(0) - const isDatasetEmpty = dataset === null || Object.keys(dataset).length === 0 + const allowedURLs = ["https://basedosdados.org", "https://staging.basedosdados.org"] + - useEffect(() => { - if (router.query?.dataset === "mundo-kaggle-olimpiadas") return window.open(`${process.env.NEXT_PUBLIC_BASE_URL_FRONTEND}/dataset/62f8cb83-ac37-48be-874b-b94dd92d3e2b`, "_self") - if (isDatasetEmpty) return router.push(`${process.env.NEXT_PUBLIC_API_URL}/dataset_redirect?dataset=${query.dataset}`) - }, []) + const isDatasetEmpty = dataset === null || Object.keys(dataset).length === 0 if(isDatasetEmpty) return @@ -117,7 +120,7 @@ export default function DatasetPage ({ dataset }) { spacing={0} > - + - + {dataset[`description${capitalize(locale)}`] || dataset.description || t('noDescription')} - + - {t('temporalCoverage')} + {t('organization')} - - {dataset.coverage || t('noCoverage')} - + + {dataset.organizations?.edges?.[0]?.node?.[`name${capitalize(locale)}`] || dataset.organizations?.edges?.[0]?.node?.name || t('noOrganization')} + + - + - {t('organization')} + {t('temporalCoverage')} - + {dataset.temporalCoverage || t('notProvided')} + + + + {!allowedURLs.includes(process.env.NEXT_PUBLIC_BASE_URL_FRONTEND) && + + + {t('spatialCoverage')} + - {dataset.organization?.[`name${capitalize(locale)}`] || dataset.organization?.name || t('noOrganization')} + {dataset?.[`spatialCoverageName${capitalize(locale)}`] + ? Object.values(dataset[`spatialCoverageName${capitalize(locale)}`]) + .sort((a, b) => a.localeCompare(b, locale)) + .join(', ') + : t('notProvided')} - - + + } diff --git a/next/pages/search.js b/next/pages/search.js index 034bc1b5..1ea95799 100644 --- a/next/pages/search.js +++ b/next/pages/search.js @@ -27,7 +27,7 @@ import { import { CheckboxFilterAccordion } from "../components/atoms/FilterAccordion"; import Checkbox from "../components/atoms/Checkbox"; import { TagFilter } from "../components/atoms/Tag"; -import Dataset from "../components/organisms/Dataset"; +import DatasetSearchCard from "../components/organisms/DatasetSearchCard"; import { MainPageTemplate } from "../components/templates/main"; import FilterIcon from "../public/img/icons/filterIcon"; @@ -43,6 +43,7 @@ export async function getStaticProps({ locale }) { export default function SearchDatasetPage() { const { t } = useTranslation('dataset') + const { locale } = useRouter() const router = useRouter() const query = router.query @@ -55,8 +56,9 @@ export default function SearchDatasetPage() { const [pageInfo, setPageInfo] = useState({page: 0, count: 0}) const [isLoading, setIsLoading] = useState(true) + const allowedURLs = ["https://basedosdados.org", "https://staging.basedosdados.org"] + async function getDatasets({q, filters, page}) { - const { locale } = router const res = await getSearchDatasets({q, filter: filters, page, locale: locale || 'pt'}) if(res === undefined) return router.push({pathname:"500"}) if(res?.count === 0) setShowEmptyState(true) @@ -292,12 +294,16 @@ export default function SearchDatasetPage() { function DatasetCard({ data }) { return ( - coverage.name) + .sort((a, b) => a.localeCompare(b, locale)) + .join(', ')} + organizations={data.organizations} tables={{ id: data?.first_table_id, number: data?.n_tables @@ -314,6 +320,7 @@ export default function SearchDatasetPage() { free: data?.contains_open_data, pro: data?.contains_closed_data }} + locale={locale} /> ) } @@ -660,6 +667,25 @@ export default function SearchDatasetPage() { + {!allowedURLs.includes(process.env.NEXT_PUBLIC_BASE_URL_FRONTEND) ? + <> + handleSelectFilter(["spatial_coverage",`${value}`])} + isLoading={!isLoading} + /> + + + : + <> + } +