diff --git a/.eslintrc.js b/.eslintrc.js deleted file mode 100644 index 580c14e7..00000000 --- a/.eslintrc.js +++ /dev/null @@ -1,20 +0,0 @@ -module.exports = { - parser: '@typescript-eslint/parser', - extends: [ - 'eslint:recommended', - 'plugin:@typescript-eslint/recommended', - 'next', - 'prettier', - ], - rules: { - 'no-unused-vars': 'off', - '@typescript-eslint/no-unused-vars': ['warn'], - '@typescript-eslint/no-explicit-any': ['off'], - 'react/display-name': 'off', - '@next/next/no-html-link-for-pages': 'off', - 'prefer-const': 'off', - '@typescript-eslint/no-var-requires': 'off', - '@next/next/no-img-element': 'off', - }, - ignorePatterns: ['**/generated/**/*.ts', 'node_modules/', 'dist/'], -} diff --git a/.eslintrc.json b/.eslintrc.json deleted file mode 100644 index 10c89b4e..00000000 --- a/.eslintrc.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "ignorePatterns": ["**/generated/**/*.ts"], - "extends": ["eslint:recommended", "next"], - "rules": { - "no-unused-vars": "off", - // "@typescript-eslint/no-unused-vars": ["warn"], - "react/display-name": "off", - "@next/next/no-html-link-for-pages": "off" - } -} diff --git a/app/[locale]/(user)/collaboratives/CollaborativesListingClient.tsx b/app/[locale]/(user)/collaboratives/CollaborativesListingClient.tsx index 9d9fdbe5..504e5ef4 100644 --- a/app/[locale]/(user)/collaboratives/CollaborativesListingClient.tsx +++ b/app/[locale]/(user)/collaboratives/CollaborativesListingClient.tsx @@ -1,52 +1,39 @@ 'use client'; -import BreadCrumbs from '@/components/BreadCrumbs'; -import { Icons } from '@/components/icons'; -import JsonLd from '@/components/JsonLd'; -import { Loading } from '@/components/loading'; +import { ComponentType, useState } from 'react'; +import Image from 'next/image'; import { graphql } from '@/gql'; import { TypeCollaborative } from '@/gql/generated/graphql'; -import { GraphQLPublic } from '@/lib/api'; -import { formatDate, generateJsonLd } from '@/lib/utils'; import { useQuery } from '@tanstack/react-query'; -import Image from 'next/image'; import { Button, Card, Icon, SearchInput, Select, Text } from 'opub-ui'; -import { useState } from 'react'; -import { cn } from '@/lib/utils'; + +import { GraphQLPublic } from '@/lib/api'; +import { cn, formatDate, generateJsonLd } from '@/lib/utils'; +import BreadCrumbs from '@/components/BreadCrumbs'; +import { Icons } from '@/components/icons'; +import JsonLd from '@/components/JsonLd'; +import { Loading } from '@/components/loading'; import Styles from '../datasets/dataset.module.scss'; // Helper function to strip markdown and HTML tags for card preview const stripMarkdown = (markdown: string): string => { if (!markdown) return ''; return markdown - // Remove code blocks first (before other replacements) .replace(/```[\s\S]*?```/g, '') - // Remove inline code .replace(/`([^`]+)`/g, '$1') - // Remove images .replace(/!\[([^\]]*)\]\([^)]+\)/g, '$1') - // Remove links .replace(/\[([^\]]+)\]\([^)]+\)/g, '$1') - // Remove headers .replace(/^#{1,6}\s+/gm, '') - // Remove bold .replace(/\*\*([^*]+)\*\*/g, '$1') .replace(/__([^_]+)__/g, '$1') - // Remove italic .replace(/\*([^*]+)\*/g, '$1') .replace(/_([^_]+)_/g, '$1') - // Remove strikethrough .replace(/~~([^~]+)~~/g, '$1') - // Remove blockquotes .replace(/^\s*>\s+/gm, '') - // Remove horizontal rules .replace(/^(-{3,}|_{3,}|\*{3,})$/gm, '') - // Remove list markers .replace(/^\s*[-*+]\s+/gm, '') .replace(/^\s*\d+\.\s+/gm, '') - // Remove HTML tags .replace(/<[^>]*>/g, '') - // Remove extra whitespace and newlines .replace(/\n\s*\n/g, '\n') .replace(/\n/g, ' ') .replace(/\s+/g, ' ') @@ -129,10 +116,7 @@ const CollaborativesListingClient = () => { console.log('Fetching collaboratives...'); try { // @ts-expect-error - Query has no variables - const result = await GraphQLPublic( - PublishedCollaboratives as any, - {} - ); + const result = await GraphQLPublic(PublishedCollaboratives as any, {}); console.log('Collaboratives result:', result); return result as { publishedCollaboratives: TypeCollaborative[] }; } catch (err) { @@ -154,13 +138,14 @@ const CollaborativesListingClient = () => { // Filter and sort collaboratives const filteredAndSortedCollaboratives = collaboratives .filter((collaborative) => { - const matchesSearch = collaborative.title?.toLowerCase().includes(searchTerm.toLowerCase()) || - collaborative.summary?.toLowerCase().includes(searchTerm.toLowerCase()); + const matchesSearch = + collaborative.title?.toLowerCase().includes(searchTerm.toLowerCase()) || + collaborative.summary?.toLowerCase().includes(searchTerm.toLowerCase()); return matchesSearch; }) .sort((a, b) => { const [field, direction] = sortBy.split('_'); - + if (field === 'title') { const comparison = (a.title || '').localeCompare(b.title || ''); return direction === 'asc' ? comparison : -comparison; @@ -175,14 +160,16 @@ const CollaborativesListingClient = () => { } return 0; }); + const jsonLd = generateJsonLd({ - '@context': 'https://schema.org', - '@type': 'WebPage', - name: 'CivicDataLab', - url: `${process.env.NEXT_PUBLIC_PLATFORM_URL}/collaboratives`, - description: - 'Solving the world\'s major challenges requires greater access to interoperable data that currently resides in silos. Data Collaboratives bring together government, academia, civil society, philanthropy, and companies to responsibly share and use data for public value. Building on trusted, long-term relationships among stakeholders, we can open access to high-impact datasets and responsible AI use-cases to generate insights for climate action, public health, gender equity, and other major shared problems, thereby advancing progress toward the Sustainable Development Goals. Our goal is to accelerate the formation of Data Collaboratives with shared governance, clear safeguards, and collaborative analytics, allowing stakeholders to harness data and AI for the public good.', - }); + '@context': 'https://schema.org', + '@type': 'WebPage', + name: 'CivicDataLab', + url: `${process.env.NEXT_PUBLIC_PLATFORM_URL}/collaboratives`, + description: + "Solving the world's major challenges requires greater access to interoperable data that currently resides in silos. Data Collaboratives bring together government, academia, civil society, philanthropy, and companies to responsibly share and use data for public value. Building on trusted, long-term relationships among stakeholders, we can open access to high-impact datasets and responsible AI use-cases to generate insights for climate action, public health, gender equity, and other major shared problems, thereby advancing progress toward the Sustainable Development Goals. Our goal is to accelerate the formation of Data Collaboratives with shared governance, clear safeguards, and collaborative analytics, allowing stakeholders to harness data and AI for the public good.", + }); + return (
@@ -193,42 +180,55 @@ const CollaborativesListingClient = () => { ]} /> <> - <> -
-
-
-
- - Our Collaboratives - - - Solving the world's major challenges requires greater access to interoperable data that currently resides in silos. Data Collaboratives bring together government, academia, civil society, philanthropy, and companies to responsibly share and use data for public value. Building on trusted, long-term relationships among stakeholders, we can open access to high-impact datasets and responsible AI use-cases to generate insights for climate action, public health, gender equity, and other major shared problems, thereby advancing progress toward the Sustainable Development Goals. Our goal is to accelerate the formation of Data Collaboratives with shared governance, clear safeguards, and collaborative analytics, allowing stakeholders to harness data and AI for the public good. - -
-
- {'collaborative'} + <> +
+
+
+
+ + Our Collaboratives + + + Solving the world's major challenges requires greater + access to interoperable data that currently resides in + silos. Data Collaboratives bring together government, + academia, civil society, philanthropy, and companies to + responsibly share and use data for public value. Building on + trusted, long-term relationships among stakeholders, we can + open access to high-impact datasets and responsible AI + use-cases to generate insights for climate action, public + health, gender equity, and other major shared problems, + thereby advancing progress toward the Sustainable + Development Goals. Our goal is to accelerate the formation + of Data Collaboratives with shared governance, clear + safeguards, and collaborative analytics, allowing + stakeholders to harness data and AI for the public good. + +
+
+ {'collaborative'} +
+
-
-
+ - - +
{/* Header Section */} @@ -236,7 +236,7 @@ const CollaborativesListingClient = () => { Explore Collaboratives - + {/* Search and Filter Section */}
{
- {isLoading? ( + {isLoading ? (
- ):error?( + ) : error ? (
Error Loading Collaboratives @@ -309,7 +309,7 @@ const CollaborativesListingClient = () => { Failed to load collaboratives. Please try again later.
- ):null} + ) : null} {/* Results Section */} {!isLoading && !error && ( @@ -317,54 +317,60 @@ const CollaborativesListingClient = () => { {/* Collaboratives Grid */} {filteredAndSortedCollaboratives.length > 0 ? (
- {filteredAndSortedCollaboratives.map((collaborative: TypeCollaborative) => ( - 0 - ? collaborative.geographies.map((geo: any) => geo.name).join(', ') - : 'N/A', - }, - ]} - href={`/collaboratives/${collaborative.slug}`} - footerContent={[ - { - icon: collaborative.sectors?.[0]?.name - ? `/Sectors/${collaborative.sectors[0].name}.svg` - : '/Sectors/default.svg', - label: 'Sectors', - }, - { - icon: collaborative.isIndividualCollaborative - ? collaborative?.user?.profilePicture - ? `${process.env.NEXT_PUBLIC_BACKEND_URL}/${collaborative.user.profilePicture.url}` - : '/profile.png' - : collaborative?.organization?.logo - ? `${process.env.NEXT_PUBLIC_BACKEND_URL}/${collaborative.organization.logo.url}` - : '/org.png', - label: 'Published by', - }, - ]} - description={stripMarkdown(collaborative.summary || '')} - /> - ))} + {filteredAndSortedCollaboratives.map( + (collaborative: TypeCollaborative) => ( + , + label: 'Geography', + value: + collaborative.geographies && + collaborative.geographies.length > 0 + ? collaborative.geographies + .map((geo: any) => geo.name) + .join(', ') + : 'N/A', + }, + ]} + href={`/collaboratives/${collaborative.slug}`} + footerContent={[ + { + icon: collaborative.sectors?.[0]?.name + ? `/Sectors/${collaborative.sectors[0].name}.svg` + : '/Sectors/default.svg', + label: 'Sectors', + }, + { + icon: collaborative.isIndividualCollaborative + ? collaborative?.user?.profilePicture + ? `${process.env.NEXT_PUBLIC_BACKEND_URL}/${collaborative.user.profilePicture.url}` + : '/profile.png' + : collaborative?.organization?.logo + ? `${process.env.NEXT_PUBLIC_BACKEND_URL}/${collaborative.organization.logo.url}` + : '/org.png', + label: 'Published by', + }, + ]} + description={stripMarkdown(collaborative.summary || '')} + /> + ) + )}
) : (
diff --git a/app/[locale]/(user)/collaboratives/[collaborativeSlug]/CollaborativeDetailsClient.tsx b/app/[locale]/(user)/collaboratives/[collaborativeSlug]/CollaborativeDetailsClient.tsx index 88bd704f..582c58dd 100644 --- a/app/[locale]/(user)/collaboratives/[collaborativeSlug]/CollaborativeDetailsClient.tsx +++ b/app/[locale]/(user)/collaboratives/[collaborativeSlug]/CollaborativeDetailsClient.tsx @@ -1,21 +1,25 @@ 'use client'; -import { graphql } from '@/gql'; -import { TypeCollaborative, TypeDataset, TypeUseCase } from '@/gql/generated/graphql'; -import { useQuery } from '@tanstack/react-query'; +import { useEffect } from 'react'; import Image from 'next/image'; import Link from 'next/link'; import { useParams } from 'next/navigation'; +import { graphql } from '@/gql'; +import { + TypeCollaborative, + TypeDataset, + TypeUseCase, +} from '@/gql/generated/graphql'; +import { useAnalytics } from '@/hooks/use-analytics'; +import { useQuery } from '@tanstack/react-query'; import { Card, Text } from 'opub-ui'; -import { useEffect } from 'react'; +import { GraphQLPublic } from '@/lib/api'; +import { formatDate, generateJsonLd } from '@/lib/utils'; import BreadCrumbs from '@/components/BreadCrumbs'; import { Icons } from '@/components/icons'; import JsonLd from '@/components/JsonLd'; import { Loading } from '@/components/loading'; -import { useAnalytics } from '@/hooks/use-analytics'; -import { GraphQLPublic } from '@/lib/api'; -import { formatDate, generateJsonLd } from '@/lib/utils'; import PrimaryDetails from '../components/Details'; import Metadata from '../components/Metadata'; @@ -223,14 +227,17 @@ const CollaborativeDetailClient = () => { } = useQuery<{ collaborativeBySlug: TypeCollaborative }>( [`fetch_CollaborativeDetails_${params.collaborativeSlug}`], async () => { - console.log('Fetching collaborative details for:', params.collaborativeSlug); - const result = await GraphQLPublic( + console.log( + 'Fetching collaborative details for:', + params.collaborativeSlug + ); + const result = (await GraphQLPublic( CollaborativeDetails as any, {}, { slug: params.collaborativeSlug, } - ) as { collaborativeBySlug: TypeCollaborative }; + )) as { collaborativeBySlug: TypeCollaborative }; return result; }, { @@ -242,25 +249,35 @@ const CollaborativeDetailClient = () => { } ); - console.log('Collaborative details query state:', { isLoading, error, data: CollaborativeDetailsData }); + console.log('Collaborative details query state:', { + isLoading, + error, + data: CollaborativeDetailsData, + }); // Track collaborative view when data is loaded useEffect(() => { if (CollaborativeDetailsData?.collaborativeBySlug) { - trackCollaborative(CollaborativeDetailsData.collaborativeBySlug.id, CollaborativeDetailsData.collaborativeBySlug.title || undefined); + trackCollaborative( + CollaborativeDetailsData.collaborativeBySlug.id, + CollaborativeDetailsData.collaborativeBySlug.title || undefined + ); } }, [CollaborativeDetailsData?.collaborativeBySlug, trackCollaborative]); - const datasets = CollaborativeDetailsData?.collaborativeBySlug?.datasets || []; // Fallback to an empty array - const useCases = CollaborativeDetailsData?.collaborativeBySlug?.useCases || []; // Fallback to an empty array + const datasets = + CollaborativeDetailsData?.collaborativeBySlug?.datasets || []; // Fallback to an empty array + const useCases = + CollaborativeDetailsData?.collaborativeBySlug?.useCases || []; // Fallback to an empty array const hasSupportingOrganizations = CollaborativeDetailsData?.collaborativeBySlug?.supportingOrganizations && - CollaborativeDetailsData?.collaborativeBySlug?.supportingOrganizations?.length > 0; + CollaborativeDetailsData?.collaborativeBySlug?.supportingOrganizations + ?.length > 0; const hasPartnerOrganizations = CollaborativeDetailsData?.collaborativeBySlug?.partnerOrganizations && - CollaborativeDetailsData?.collaborativeBySlug?.partnerOrganizations?.length > 0; - + CollaborativeDetailsData?.collaborativeBySlug?.partnerOrganizations + ?.length > 0; const jsonLd = generateJsonLd({ '@context': 'https://schema.org', @@ -292,7 +309,8 @@ const CollaborativeDetailClient = () => { Error Loading Collaborative - {(error as any)?.message?.includes('401') || (error as any)?.message?.includes('403') + {(error as any)?.message?.includes('401') || + (error as any)?.message?.includes('403') ? 'You do not have permission to view this collaborative. Please log in or contact the administrator.' : 'Failed to load collaborative details. Please try again later.'} @@ -313,168 +331,181 @@ const CollaborativeDetailClient = () => { data={[ { href: '/', label: 'Home' }, { href: '/collaboratives', label: 'Collaboratives' }, - { href: '#', label: CollaborativeDetailsData?.collaborativeBySlug?.title || '' }, + { + href: '#', + label: + CollaborativeDetailsData?.collaborativeBySlug?.title || '', + }, ]} />
- +
{(hasSupportingOrganizations || hasPartnerOrganizations) && ( -
-
- {hasSupportingOrganizations && ( -
- - Supporters - -
- {CollaborativeDetailsData?.collaborativeBySlug?.supportingOrganizations?.map( - (org: any) => ( - -
- {org.name} -
- - ) - )} +
+
+ {hasSupportingOrganizations && ( +
+ + Supporters + +
+ {CollaborativeDetailsData?.collaborativeBySlug?.supportingOrganizations?.map( + (org: any) => ( + +
+ {org.name} +
+ + ) + )} +
-
- )} - {hasPartnerOrganizations && ( -
- - Partners - -
- {CollaborativeDetailsData?.collaborativeBySlug?.partnerOrganizations?.map( - (org: any) => ( - -
- {org.name} -
- - ) - )} + )} + {hasPartnerOrganizations && ( +
+ + Partners + +
+ {CollaborativeDetailsData?.collaborativeBySlug?.partnerOrganizations?.map( + (org: any) => ( + +
+ {org.name} +
+ + ) + )} +
-
- )} + )} +
-
- )} + )}
{/* Use Cases Section */} - {useCases.length > 0 && ( -
-
- Use Cases - - Use Cases associated with this Collaborative - -
-
- {useCases.map((useCase: TypeUseCase) => { - const image = useCase.isIndividualUsecase - ? useCase?.user?.profilePicture - ? `${process.env.NEXT_PUBLIC_BACKEND_URL}/${useCase.user.profilePicture.url}` - : '/profile.png' - : useCase?.organization?.logo - ? `${process.env.NEXT_PUBLIC_BACKEND_URL}/${useCase.organization.logo.url}` - : '/org.png'; + {useCases.length > 0 && ( +
+
+ Use Cases + + Use Cases associated with this Collaborative + +
+
+ {useCases.map((useCase: TypeUseCase) => { + const image = useCase.isIndividualUsecase + ? useCase?.user?.profilePicture + ? `${process.env.NEXT_PUBLIC_BACKEND_URL}/${useCase.user.profilePicture.url}` + : '/profile.png' + : useCase?.organization?.logo + ? `${process.env.NEXT_PUBLIC_BACKEND_URL}/${useCase.organization.logo.url}` + : '/org.png'; - const Geography = useCase.geographies && useCase.geographies.length > 0 - ? useCase.geographies.map((geo: any) => geo.name).join(', ') - : null; + const Geography = + useCase.geographies && useCase.geographies.length > 0 + ? useCase.geographies + .map((geo: any) => geo.name) + .join(', ') + : null; - const MetadataContent = [ - { - icon: Icons.calendar, - label: 'Date', - value: formatDate(useCase.modified), - tooltip: 'Date', - }, - ]; + const MetadataContent = [ + { + icon: Icons.calendar as any, + label: 'Date', + value: formatDate(useCase.modified), + tooltip: 'Date', + }, + ]; - if (Geography) { - MetadataContent.push({ - icon: Icons.globe, - label: 'Geography', - value: Geography, - tooltip: 'Geography', - }); - } + if (Geography) { + MetadataContent.push({ + icon: Icons.globe as any, + label: 'Geography', + value: Geography, + tooltip: 'Geography', + }); + } - const FooterContent = [ - { - icon: useCase.sectors && useCase.sectors[0]?.name - ? `/Sectors/${useCase.sectors[0].name}.svg` - : '/Sectors/default.svg', - label: 'Sectors', - tooltip: useCase.sectors?.[0]?.name || 'Sector', - }, - { - icon: image, - label: 'Published by', - tooltip: useCase.isIndividualUsecase - ? useCase.user?.fullName - : useCase.organization?.name, - }, - ]; + const FooterContent = [ + { + icon: + useCase.sectors && useCase.sectors[0]?.name + ? `/Sectors/${useCase.sectors[0].name}.svg` + : '/Sectors/default.svg', + label: 'Sectors', + tooltip: useCase.sectors?.[0]?.name || 'Sector', + }, + { + icon: image, + label: 'Published by', + tooltip: useCase.isIndividualUsecase + ? useCase.user?.fullName + : useCase.organization?.name, + }, + ]; - const commonProps = { - title: useCase.title || '', - description: useCase.summary || '', - metadataContent: MetadataContent, - tag: useCase.tags?.map((t: any) => t.value) || [], - footerContent: FooterContent, - imageUrl: '', - }; + const commonProps = { + title: useCase.title || '', + description: useCase.summary || '', + metadataContent: MetadataContent, + tag: useCase.tags?.map((t: any) => t.value) || [], + footerContent: FooterContent, + imageUrl: '', + }; - if (useCase.logo) { - commonProps.imageUrl = `${process.env.NEXT_PUBLIC_BACKEND_URL}/${useCase.logo.path.replace('/code/files/', '')}`; - } + if (useCase.logo) { + commonProps.imageUrl = `${process.env.NEXT_PUBLIC_BACKEND_URL}/${useCase.logo.path.replace('/code/files/', '')}`; + } - return ( - - ); - })} + return ( + + ); + })} +
-
- )} + )} {/* Datasets Section */}
- Datasets in this Collaborative + + Datasets in this Collaborative + Explore datasets related to this collaborative{' '} @@ -489,21 +520,24 @@ const CollaborativeDetailClient = () => { iconColor={'warning'} metadataContent={[ { - icon: Icons.calendar, + icon: Icons.calendar as any, label: 'Date', value: formatDate(dataset.modified), }, { - icon: Icons.download, + icon: Icons.download as any, label: 'Download', value: dataset.downloadCount.toString(), }, { - icon: Icons.globe, + icon: Icons.globe as any, label: 'Geography', value: - dataset.geographies && dataset.geographies.length > 0 - ? dataset.geographies.map((geo: any) => geo.name).join(', ') + dataset.geographies && + dataset.geographies.length > 0 + ? dataset.geographies + .map((geo: any) => geo.name) + .join(', ') : '', }, ]} @@ -530,7 +564,6 @@ const CollaborativeDetailClient = () => {
- )}
diff --git a/app/[locale]/(user)/collaboratives/[collaborativeSlug]/page.tsx b/app/[locale]/(user)/collaboratives/[collaborativeSlug]/page.tsx index 4be3188a..2fc14eed 100644 --- a/app/[locale]/(user)/collaboratives/[collaborativeSlug]/page.tsx +++ b/app/[locale]/(user)/collaboratives/[collaborativeSlug]/page.tsx @@ -26,10 +26,15 @@ const CollaborativeInfoQuery = graphql(` export async function generateMetadata({ params, }: { - params: { collaborativeSlug: string }; + params: Promise<{ collaborativeSlug: string }>; }): Promise { + const { collaborativeSlug } = await params; try { - const data = await GraphQLPublic(CollaborativeInfoQuery, {}, { pk: params.collaborativeSlug }); + const data = await GraphQLPublic( + CollaborativeInfoQuery, + {}, + { pk: collaborativeSlug } + ); const Collaborative = data?.collaborative; return generatePageMetadata({ @@ -41,7 +46,7 @@ export async function generateMetadata({ openGraph: { type: 'article', locale: 'en_US', - url: `${process.env.NEXT_PUBLIC_PLATFORM_URL}/collaboratives/${params.collaborativeSlug}`, + url: `${process.env.NEXT_PUBLIC_PLATFORM_URL}/collaboratives/${collaborativeSlug}`, title: `${Collaborative?.title} | Collaborative Data | CivicDataSpace`, description: Collaborative?.summary || @@ -59,7 +64,7 @@ export async function generateMetadata({ openGraph: { type: 'article', locale: 'en_US', - url: `${process.env.NEXT_PUBLIC_PLATFORM_URL}/collaboratives/${params.collaborativeSlug}`, + url: `${process.env.NEXT_PUBLIC_PLATFORM_URL}/collaboratives/${collaborativeSlug}`, title: `Collaborative Details | CivicDataSpace`, description: `Explore open data and curated datasets in this collaborative.`, siteName: 'CivicDataSpace', diff --git a/app/[locale]/(user)/components/Datasets.tsx b/app/[locale]/(user)/components/Datasets.tsx index fd04b611..822f6eac 100644 --- a/app/[locale]/(user)/components/Datasets.tsx +++ b/app/[locale]/(user)/components/Datasets.tsx @@ -100,25 +100,29 @@ const Datasets = () => { description={item.description} metadataContent={[ { - icon: Icons.calendar, + icon: Icons.calendar as any, label: 'Date', - value: new Date(item.modified).toLocaleDateString('en-US', { - day: 'numeric', - month: 'long', - year: 'numeric', - }), + value: new Date(item.modified).toLocaleDateString( + 'en-US', + { + day: 'numeric', + month: 'long', + year: 'numeric', + } + ), }, { - icon: Icons.download, + icon: Icons.download as any, label: 'Download', value: item.download_count.toString(), }, { icon: Icons.globe, label: 'Geography', - value: item.geographies?.length > 0 - ? item.geographies.join(', ') - : 'Not specified', + value: + item.geographies?.length > 0 + ? item.geographies.join(', ') + : 'Not specified', }, ]} tag={item.tags} diff --git a/app/[locale]/(user)/components/ListingComponent.tsx b/app/[locale]/(user)/components/ListingComponent.tsx index 96c3a297..cc25d274 100644 --- a/app/[locale]/(user)/components/ListingComponent.tsx +++ b/app/[locale]/(user)/components/ListingComponent.tsx @@ -1,8 +1,11 @@ -'use client' +'use client'; -import GraphqlPagination from '@/app/[locale]/dashboard/components/GraphqlPagination/graphqlPagination'; +import React, { useEffect, useMemo, useReducer, useRef, useState } from 'react'; import Image from 'next/image'; import { useRouter } from 'next/navigation'; +import GraphqlPagination from '@/app/[locale]/dashboard/components/GraphqlPagination/graphqlPagination'; +import { fetchData } from '@/fetch'; +import { useTourTrigger } from '@/hooks/use-tour-trigger'; import { Button, ButtonGroup, @@ -14,53 +17,52 @@ import { Text, Tray, } from 'opub-ui'; -import React, { useEffect, useMemo, useReducer, useRef, useState } from 'react'; +import { cn, formatDate } from '@/lib/utils'; import BreadCrumbs from '@/components/BreadCrumbs'; import { Icons } from '@/components/icons'; import { Loading } from '@/components/loading'; -import { fetchData } from '@/fetch'; -import { cn, formatDate } from '@/lib/utils'; import Filter from '../datasets/components/FIlter/Filter'; import Styles from '../datasets/dataset.module.scss'; -import { useTourTrigger } from '@/hooks/use-tour-trigger'; // Helper function to strip markdown and HTML tags for card preview const stripMarkdown = (markdown: string): string => { if (!markdown) return ''; - return markdown - // Remove code blocks first (before other replacements) - .replace(/```[\s\S]*?```/g, '') - // Remove inline code - .replace(/`([^`]+)`/g, '$1') - // Remove images - .replace(/!\[([^\]]*)\]\([^)]+\)/g, '$1') - // Remove links - .replace(/\[([^\]]+)\]\([^)]+\)/g, '$1') - // Remove headers - .replace(/^#{1,6}\s+/gm, '') - // Remove bold - .replace(/\*\*([^*]+)\*\*/g, '$1') - .replace(/__([^_]+)__/g, '$1') - // Remove italic - .replace(/\*([^*]+)\*/g, '$1') - .replace(/_([^_]+)_/g, '$1') - // Remove strikethrough - .replace(/~~([^~]+)~~/g, '$1') - // Remove blockquotes - .replace(/^\s*>\s+/gm, '') - // Remove horizontal rules - .replace(/^(-{3,}|_{3,}|\*{3,})$/gm, '') - // Remove list markers - .replace(/^\s*[-*+]\s+/gm, '') - .replace(/^\s*\d+\.\s+/gm, '') - // Remove HTML tags - .replace(/<[^>]*>/g, '') - // Remove extra whitespace and newlines - .replace(/\n\s*\n/g, '\n') - .replace(/\n/g, ' ') - .replace(/\s+/g, ' ') - .trim(); + return ( + markdown + // Remove code blocks first (before other replacements) + .replace(/```[\s\S]*?```/g, '') + // Remove inline code + .replace(/`([^`]+)`/g, '$1') + // Remove images + .replace(/!\[([^\]]*)\]\([^)]+\)/g, '$1') + // Remove links + .replace(/\[([^\]]+)\]\([^)]+\)/g, '$1') + // Remove headers + .replace(/^#{1,6}\s+/gm, '') + // Remove bold + .replace(/\*\*([^*]+)\*\*/g, '$1') + .replace(/__([^_]+)__/g, '$1') + // Remove italic + .replace(/\*([^*]+)\*/g, '$1') + .replace(/_([^_]+)_/g, '$1') + // Remove strikethrough + .replace(/~~([^~]+)~~/g, '$1') + // Remove blockquotes + .replace(/^\s*>\s+/gm, '') + // Remove horizontal rules + .replace(/^(-{3,}|_{3,}|\*{3,})$/gm, '') + // Remove list markers + .replace(/^\s*[-*+]\s+/gm, '') + .replace(/^\s*\d+\.\s+/gm, '') + // Remove HTML tags + .replace(/<[^>]*>/g, '') + // Remove extra whitespace and newlines + .replace(/\n\s*\n/g, '\n') + .replace(/\n/g, ' ') + .replace(/\s+/g, ' ') + .trim() + ); }; // Interfaces @@ -170,7 +172,9 @@ const useUrlParams = ( // Merge locked filters with URL filters Object.entries(lockedFilters).forEach(([category, values]) => { if (values.length > 0) { - filters[category] = Array.from(new Set([...(filters[category] || []), ...values])); + filters[category] = Array.from( + new Set([...(filters[category] || []), ...values]) + ); } }); @@ -258,7 +262,7 @@ const ListingComponent: React.FC = ({ lockedFilters = {}, }) => { useTourTrigger(true, 1500); - + const [facets, setFacets] = useState<{ results: any[]; total: number; @@ -273,7 +277,10 @@ const ListingComponent: React.FC = ({ const datasetDetails = facets?.results ?? []; // Stabilize lockedFilters reference to prevent infinite loops - const stableLockedFilters = useMemo(() => lockedFilters, [JSON.stringify(lockedFilters)]); + const stableLockedFilters = useMemo( + () => lockedFilters, + [JSON.stringify(lockedFilters)] + ); useUrlParams(queryParams, setQueryParams, setVariables, stableLockedFilters); const latestFetchId = useRef(0); @@ -282,7 +289,7 @@ const ListingComponent: React.FC = ({ if (variables) { const currentFetchId = ++latestFetchId.current; - fetchData(type,variables) + fetchData(type, variables) .then((res) => { // Only set if this is the latest call if (currentFetchId === latestFetchId.current) { @@ -341,7 +348,7 @@ const ListingComponent: React.FC = ({ label: bucket.key, value: bucket.key, })); - } + } // Handle key-value object format (current backend format) else if (value && typeof value === 'object' && !Array.isArray(value)) { acc[key] = Object.entries(value).map(([label, count]) => ({ @@ -400,7 +407,10 @@ const ListingComponent: React.FC = ({
-
+
= ({ .filter((value) => category !== 'sort') .map((value) => { // Check if this filter value is locked - const isLocked = stableLockedFilters[category]?.includes(value); + const isLocked = + stableLockedFilters[category]?.includes(value); return ( handleRemoveFilter(category, value)} + onRemove={ + isLocked + ? undefined + : () => handleRemoveFilter(category, value) + } > {value} @@ -552,18 +567,18 @@ const ListingComponent: React.FC = ({ : item?.organization?.logo ? `${process.env.NEXT_PUBLIC_BACKEND_URL}/${item.organization.logo}` : '/org.png'; - - const geographies = item.geographies && item.geographies.length > 0 - ? item.geographies - : null; - const sdgs = item.sdgs && item.sdgs.length > 0 - ? item.sdgs - : null; + const geographies = + item.geographies && item.geographies.length > 0 + ? item.geographies + : null; + + const sdgs = + item.sdgs && item.sdgs.length > 0 ? item.sdgs : null; const MetadataContent = [ { - icon: Icons.calendar, + icon: Icons.calendar as any, label: 'Date', value: formatDate(item.modified), tooltip: 'Date', @@ -572,7 +587,7 @@ const ListingComponent: React.FC = ({ if (item.download_count > 0) { MetadataContent.push({ - icon: Icons.download, + icon: Icons.download as any, label: 'Download', value: item.download_count?.toString() || '0', tooltip: 'Download', @@ -584,7 +599,7 @@ const ListingComponent: React.FC = ({ const geoDisplay = geographies.join(', '); MetadataContent.push({ - icon: Icons.globe, + icon: Icons.globe as any, label: 'Geography', value: geoDisplay, tooltip: geoDisplay, @@ -593,10 +608,12 @@ const ListingComponent: React.FC = ({ if (sdgs && sdgs.length > 0) { // Format SDGs for display - const sdgDisplay = sdgs.map((sdg: any) => `${sdg.code} - ${sdg.name}`).join(', '); + const sdgDisplay = sdgs + .map((sdg: any) => `${sdg.code} - ${sdg.name}`) + .join(', '); MetadataContent.push({ - icon: Icons.target, + icon: Icons.target as any, label: 'SDG Goals', value: sdgDisplay, tooltip: sdgDisplay, @@ -605,7 +622,7 @@ const ListingComponent: React.FC = ({ if (item.has_charts && view === 'expanded') { MetadataContent.push({ - icon: Icons.chart, + icon: Icons.chart as any, label: '', value: 'With Charts', tooltip: 'Charts', @@ -614,21 +631,21 @@ const ListingComponent: React.FC = ({ const FooterContent = [ { - icon: `/Sectors/${item.sectors?.[0]}.svg`, + icon: `/Sectors/${item.sectors?.[0]}.svg` as any, label: 'Sectors', tooltip: `${item.sectors?.[0]}`, }, ...(item.has_charts && view !== 'expanded' ? [ { - icon: `/chart-bar.svg`, + icon: `/chart-bar.svg` as any, label: 'Charts', tooltip: 'Charts', }, ] : []), { - icon: image, + icon: image as any, label: 'Published by', tooltip: `${item.is_individual_dataset ? item.user?.name : item.organization?.name}`, }, @@ -657,7 +674,13 @@ const ListingComponent: React.FC = ({ } iconColor="warning" href={`${redirectionURL}/${item.id}`} - data-tour={index === 0 && type === 'dataset' ? 'dataset-card' : index === 0 && type === 'usecase' ? 'usecase-card' : undefined} + data-tour={ + index === 0 && type === 'dataset' + ? 'dataset-card' + : index === 0 && type === 'usecase' + ? 'usecase-card' + : undefined + } /> ); })} diff --git a/app/[locale]/(user)/components/UseCases.tsx b/app/[locale]/(user)/components/UseCases.tsx index f748671c..b13db1f8 100644 --- a/app/[locale]/(user)/components/UseCases.tsx +++ b/app/[locale]/(user)/components/UseCases.tsx @@ -99,7 +99,7 @@ const UseCasesListingPage = () => { return (
-
+
Recent UseCases @@ -148,31 +148,34 @@ const UseCasesListingPage = () => { href={`/usecases/${item.id}`} metadataContent={[ { - icon: Icons.calendar, + icon: Icons.calendar as any, label: 'Date', value: formatDate(item.modified), }, { - icon: Icons.globe, + icon: Icons.globe as any, label: 'Geography', - value: item.geographies?.length > 0 - ? item.geographies.map((geo: any) => geo.name).join(', ') - : 'Not specified', + value: + item.geographies?.length > 0 + ? item.geographies + .map((geo: any) => geo.name) + .join(', ') + : 'Not specified', }, ]} footerContent={[ { - icon: `/Sectors/${item?.sectors[0]?.name}.svg`, + icon: `/Sectors/${item?.sectors[0]?.name}.svg` as any, label: 'Sectors', }, { icon: item.isIndividualUsecase - ? item?.user?.profilePicture + ? (item?.user?.profilePicture as any) ? `${process.env.NEXT_PUBLIC_BACKEND_URL}/${item.user.profilePicture.url}` : '/profile.png' : item?.organization?.logo ? `${process.env.NEXT_PUBLIC_BACKEND_URL}/${item.organization.logo.url}` - : '/org.png', + : ('/org.png' as any), label: 'Published by', }, ]} diff --git a/app/[locale]/(user)/datasets/[datasetIdentifier]/components/SimilarDatasets/index.tsx b/app/[locale]/(user)/datasets/[datasetIdentifier]/components/SimilarDatasets/index.tsx index 9290e06c..316ac1ab 100644 --- a/app/[locale]/(user)/datasets/[datasetIdentifier]/components/SimilarDatasets/index.tsx +++ b/app/[locale]/(user)/datasets/[datasetIdentifier]/components/SimilarDatasets/index.tsx @@ -126,17 +126,17 @@ const SimilarDatasets: React.FC = () => { description={item.description} metadataContent={[ { - icon: Icons.calendar, + icon: Icons.calendar as any, label: 'Date', value: '19 July 2024', }, { - icon: Icons.download, + icon: Icons.download as any, label: 'Download', value: item.downloadCount.toString(), }, { - icon: Icons.globe, + icon: Icons.globe as any, label: 'Geography', value: item.geographies.join(', '), }, @@ -145,17 +145,17 @@ const SimilarDatasets: React.FC = () => { formats={item.formats} footerContent={[ { - icon: `/Sectors/${item.sectors[0]?.name}.svg`, + icon: `/Sectors/${item.sectors[0]?.name}.svg` as any, label: 'Sectors', }, { icon: item.isIndividualDataset - ? item?.user?.profilePicture + ? (item?.user?.profilePicture as any) ? `${process.env.NEXT_PUBLIC_BACKEND_URL}/${item.user.profilePicture.url}` - : '/profile.png' - : item?.organization?.logo + : ('/profile.png' as any) + : (item?.organization?.logo as any) ? `${process.env.NEXT_PUBLIC_BACKEND_URL}/${item.organization.logo.url}` - : '/org.png', + : ('/org.png' as any), label: 'Published by', }, ]} diff --git a/app/[locale]/(user)/datasets/[datasetIdentifier]/page.tsx b/app/[locale]/(user)/datasets/[datasetIdentifier]/page.tsx index f4803aa1..e38dd185 100644 --- a/app/[locale]/(user)/datasets/[datasetIdentifier]/page.tsx +++ b/app/[locale]/(user)/datasets/[datasetIdentifier]/page.tsx @@ -21,13 +21,14 @@ const datasetMetaQuery: any = graphql(` export async function generateMetadata({ params, }: { - params: { datasetIdentifier: string }; + params: Promise<{ datasetIdentifier: string }>; }) { + const { datasetIdentifier } = await params; try { const res: any = await GraphQL( datasetMetaQuery, {}, - { datasetId: params.datasetIdentifier } + { datasetId: datasetIdentifier } ); const dataset = res?.getDataset; @@ -38,7 +39,7 @@ export async function generateMetadata({ openGraph: { type: 'dataset', locale: 'en_US', - url: `${process.env.NEXT_PUBLIC_PLATFORM_URL}/datasets/${params.datasetIdentifier}`, + url: `${process.env.NEXT_PUBLIC_PLATFORM_URL}/datasets/${datasetIdentifier}`, title: dataset?.title, description: dataset?.description, siteName: 'CivicDataSpace', @@ -51,10 +52,12 @@ export async function generateMetadata({ } } -export default function Page({ +export default async function Page({ params, }: { - params: { datasetIdentifier: string }; + params: Promise<{ datasetIdentifier: string }>; }) { - return ; + const { datasetIdentifier } = await params; + + return ; } diff --git a/app/[locale]/(user)/publishers/[publisherSlug]/page.tsx b/app/[locale]/(user)/publishers/[publisherSlug]/page.tsx index b527b770..913afd5e 100644 --- a/app/[locale]/(user)/publishers/[publisherSlug]/page.tsx +++ b/app/[locale]/(user)/publishers/[publisherSlug]/page.tsx @@ -21,12 +21,13 @@ const userInfo = graphql(` export async function generateMetadata({ params, }: { - params: { publisherSlug: string }; + params: Promise<{ publisherSlug: string }>; }): Promise { + const { publisherSlug } = await params; const data = await GraphQL( userInfo, {}, - { userId: extractPublisherId(params.publisherSlug) } + { userId: extractPublisherId(publisherSlug) } ); const user = data.userById; @@ -49,7 +50,7 @@ export async function generateMetadata({ user?.bio || 'Explore datasets and use cases by this publisher.', type: 'profile', siteName: 'CivicDataSpace', - url: `${process.env.NEXT_PUBLIC_PLATFORM_URL}/publishers/${params.publisherSlug}`, + url: `${process.env.NEXT_PUBLIC_PLATFORM_URL}/publishers/${publisherSlug}`, image: user?.profilePicture?.url ? `${process.env.NEXT_PUBLIC_BACKEND_URL}/${user.profilePicture.url}` : `${process.env.NEXT_PUBLIC_PLATFORM_URL}/og.png`, @@ -58,14 +59,13 @@ export async function generateMetadata({ }); } -export default function PublisherPage({ +export default async function PublisherPage({ params, }: { - params: { publisherSlug: string }; + params: Promise<{ publisherSlug: string }>; }) { + const { publisherSlug } = await params; return ( - + ); } diff --git a/app/[locale]/(user)/publishers/components/Datasets.tsx b/app/[locale]/(user)/publishers/components/Datasets.tsx index 392a1208..3e54ec0b 100644 --- a/app/[locale]/(user)/publishers/components/Datasets.tsx +++ b/app/[locale]/(user)/publishers/components/Datasets.tsx @@ -155,17 +155,17 @@ const Datasets = ({ type }: { type: 'organization' | 'Publisher' }) => { description={item.description} metadataContent={[ { - icon: Icons.calendar, + icon: Icons.calendar as any, label: 'Date', value: '19 July 2024', }, { - icon: Icons.download, + icon: Icons.download as any, label: 'Download', value: item.downloadCount.toString(), }, { - icon: Icons.globe, + icon: Icons.globe as any, label: 'Geography', value: 'India', }, diff --git a/app/[locale]/(user)/publishers/components/UseCases.tsx b/app/[locale]/(user)/publishers/components/UseCases.tsx index be612051..cfb2b7eb 100644 --- a/app/[locale]/(user)/publishers/components/UseCases.tsx +++ b/app/[locale]/(user)/publishers/components/UseCases.tsx @@ -152,12 +152,12 @@ const UseCases = ({ type }: { type: 'organization' | 'Publisher' }) => { href={`/usecases/${item.id}`} metadataContent={[ { - icon: Icons.calendar, + icon: Icons.calendar as any, label: 'Date', value: formatDate(item.modified), }, { - icon: Icons.globe, + icon: Icons.globe as any, label: 'Geography', value: item.metadata?.find( (meta: any) => meta.metadataItem?.label === 'Geography' @@ -166,7 +166,7 @@ const UseCases = ({ type }: { type: 'organization' | 'Publisher' }) => { ]} footerContent={[ { - icon: `/Sectors/${item?.sectors[0]?.name}.svg`, + icon: `/Sectors/${item?.sectors[0]?.name}.svg` as any, label: 'Sectors', }, { diff --git a/app/[locale]/(user)/publishers/organization/[organizationSlug]/page.tsx b/app/[locale]/(user)/publishers/organization/[organizationSlug]/page.tsx index 5e9f25a9..5f6962cd 100644 --- a/app/[locale]/(user)/publishers/organization/[organizationSlug]/page.tsx +++ b/app/[locale]/(user)/publishers/organization/[organizationSlug]/page.tsx @@ -21,12 +21,13 @@ const orgDataQuery = graphql(` export async function generateMetadata({ params, }: { - params: { organizationSlug: string }; + params: Promise<{ organizationSlug: string }>; }): Promise { + const { organizationSlug } = await params; const data = await GraphQL( orgDataQuery, {}, - { id: extractPublisherId(params.organizationSlug) } + { id: extractPublisherId(organizationSlug) } ); const org = data.organization; @@ -49,7 +50,7 @@ export async function generateMetadata({ org?.description || 'Explore datasets and use cases by this publisher.', type: 'profile', siteName: 'CivicDataSpace', - url: `${process.env.NEXT_PUBLIC_PLATFORM_URL}/publishers/${params.organizationSlug}`, + url: `${process.env.NEXT_PUBLIC_PLATFORM_URL}/publishers/${organizationSlug}`, image: org?.logo?.url ? `${process.env.NEXT_PUBLIC_BACKEND_URL}/${org.logo.url}` : `${process.env.NEXT_PUBLIC_PLATFORM_URL}/og.png`, @@ -58,14 +59,15 @@ export async function generateMetadata({ }); } -export default function OrgPage({ +export default async function OrgPage({ params, }: { - params: { organizationSlug: string }; + params: Promise<{ organizationSlug: string }>; }) { + const { organizationSlug } = await params; return ( ); } diff --git a/app/[locale]/(user)/search/components/UnifiedListingComponent.tsx b/app/[locale]/(user)/search/components/UnifiedListingComponent.tsx index 036aae95..67226cdd 100644 --- a/app/[locale]/(user)/search/components/UnifiedListingComponent.tsx +++ b/app/[locale]/(user)/search/components/UnifiedListingComponent.tsx @@ -1,7 +1,8 @@ -'use client' +'use client'; -import GraphqlPagination from '@/app/[locale]/dashboard/components/GraphqlPagination/graphqlPagination'; +import React, { useEffect, useReducer, useRef, useState } from 'react'; import { useRouter } from 'next/navigation'; +import GraphqlPagination from '@/app/[locale]/dashboard/components/GraphqlPagination/graphqlPagination'; import { Button, ButtonGroup, @@ -13,12 +14,11 @@ import { Text, Tray, } from 'opub-ui'; -import React, { useEffect, useReducer, useRef, useState } from 'react'; +import { cn, formatDate } from '@/lib/utils'; import BreadCrumbs from '@/components/BreadCrumbs'; import { Icons } from '@/components/icons'; import { Loading } from '@/components/loading'; -import { cn, formatDate } from '@/lib/utils'; import Filter from '../../datasets/components/FIlter/Filter'; import Styles from '../../datasets/dataset.module.scss'; @@ -186,7 +186,7 @@ const useUrlParams = ( const typesParam = queryParams.types ? `&types=${encodeURIComponent(queryParams.types)}` : ''; - + const variablesString = `?${filtersString}&size=${queryParams.pageSize}&page=${queryParams.currentPage}${searchParam}${sortParam}${orderParam}${typesParam}`; setVariables(variablesString); @@ -222,7 +222,7 @@ const useUrlParams = ( } else { currentUrl.searchParams.delete('types'); } - + router.replace(currentUrl.toString()); }, [queryParams, setVariables, router]); }; @@ -324,17 +324,20 @@ const UnifiedListingComponent: React.FC = ({ const aggregations: Aggregations = facets?.aggregations || {}; const filterOptions = Object.entries(aggregations).reduce( - (acc: Record, [key, _value]) => { + ( + acc: Record, + [key, _value] + ) => { // Skip the 'types' aggregation from filters if (key === 'types') return acc; - + // Check if _value exists and has buckets array (Elasticsearch format) if (_value && _value.buckets && Array.isArray(_value.buckets)) { acc[key] = _value.buckets.map((bucket) => ({ label: bucket.key, value: bucket.key, })); - } + } // Handle key-value object format (current backend format) else if (_value && typeof _value === 'object' && !Array.isArray(_value)) { acc[key] = Object.entries(_value).map(([label]) => ({ @@ -364,7 +367,6 @@ const UnifiedListingComponent: React.FC = ({ } }; - return (
{breadcrumbData && } @@ -382,47 +384,69 @@ const UnifiedListingComponent: React.FC = ({
{/* Type Filter Buttons */} -
+
@@ -525,7 +549,7 @@ const UnifiedListingComponent: React.FC = ({
- + {Object.entries(queryParams.filters).some( ([key, value]) => key !== 'sort' && Array.isArray(value) && value.length > 0 @@ -560,11 +584,11 @@ const UnifiedListingComponent: React.FC = ({ > {results.map((item: any) => { // Determine if it's individual or organization - const isIndividual = - item.is_individual_dataset || - item.is_individual_usecase || + const isIndividual = + item.is_individual_dataset || + item.is_individual_usecase || item.is_individual_model; - + const image = isIndividual ? item?.user?.profile_picture ? `${process.env.NEXT_PUBLIC_BACKEND_URL}/${item.user.profile_picture}` @@ -572,18 +596,18 @@ const UnifiedListingComponent: React.FC = ({ : item?.organization?.logo ? `${process.env.NEXT_PUBLIC_BACKEND_URL}/${item.organization.logo}` : '/org.png'; - - const geographies = item.geographies && item.geographies.length > 0 - ? item.geographies - : null; - const sdgs = item.sdgs && item.sdgs.length > 0 - ? item.sdgs - : null; + const geographies = + item.geographies && item.geographies.length > 0 + ? item.geographies + : null; + + const sdgs = + item.sdgs && item.sdgs.length > 0 ? item.sdgs : null; const MetadataContent = [ { - icon: Icons.calendar, + icon: Icons.calendar as any, label: 'Date', value: formatDate(item.modified || item.updated_at), tooltip: 'Date', @@ -593,7 +617,7 @@ const UnifiedListingComponent: React.FC = ({ // Type-specific metadata for datasets if (item.type === 'dataset' && item.download_count > 0) { MetadataContent.push({ - icon: Icons.download, + icon: Icons.download as any, label: 'Download', value: item.download_count?.toString() || '0', tooltip: 'Download', @@ -603,7 +627,7 @@ const UnifiedListingComponent: React.FC = ({ if (geographies && geographies.length > 0) { const geoDisplay = geographies.join(', '); MetadataContent.push({ - icon: Icons.globe, + icon: Icons.globe as any, label: 'Geography', value: geoDisplay, tooltip: geoDisplay, @@ -612,9 +636,11 @@ const UnifiedListingComponent: React.FC = ({ // Add SDGs for datasets if (item.type === 'dataset' && sdgs && sdgs.length > 0) { - const sdgDisplay = sdgs.map((sdg: any) => `${sdg.code} - ${sdg.name}`).join(', '); + const sdgDisplay = sdgs + .map((sdg: any) => `${sdg.code} - ${sdg.name}`) + .join(', '); MetadataContent.push({ - icon: Icons.star, + icon: Icons.star as any, label: 'SDG Goals', value: sdgDisplay, tooltip: sdgDisplay, @@ -622,7 +648,11 @@ const UnifiedListingComponent: React.FC = ({ } // Add charts indicator for datasets - if (item.type === 'dataset' && item.has_charts && view === 'expanded') { + if ( + item.type === 'dataset' && + item.has_charts && + view === 'expanded' + ) { MetadataContent.push({ icon: Icons.chart, label: '', @@ -633,23 +663,27 @@ const UnifiedListingComponent: React.FC = ({ const FooterContent = [ ...(item.sectors && item.sectors.length > 0 - ? [{ - icon: `/Sectors/${item.sectors?.[0]}.svg`, - label: 'Sectors', - tooltip: `${item.sectors?.[0]}`, - }] + ? [ + { + icon: `/Sectors/${item.sectors?.[0]}.svg` as any, + label: 'Sectors', + tooltip: `${item.sectors?.[0]}`, + }, + ] : []), - ...(item.type === 'dataset' && item.has_charts && view !== 'expanded' + ...(item.type === 'dataset' && + item.has_charts && + view !== 'expanded' ? [ { - icon: `/chart-bar.svg`, + icon: `/chart-bar.svg` as any, label: 'Charts', tooltip: 'Charts', }, ] : []), { - icon: image, + icon: image as any, label: 'Published by', tooltip: `${isIndividual ? item.user?.name : item.organization?.name}`, }, diff --git a/app/[locale]/(user)/sectors/[sectorSlug]/page.tsx b/app/[locale]/(user)/sectors/[sectorSlug]/page.tsx index b81e5bdd..eaf319b8 100644 --- a/app/[locale]/(user)/sectors/[sectorSlug]/page.tsx +++ b/app/[locale]/(user)/sectors/[sectorSlug]/page.tsx @@ -20,12 +20,13 @@ const sectorQueryDoc = graphql(` export async function generateMetadata({ params, }: { - params: { sectorSlug: string }; + params: Promise<{ sectorSlug: string }>; }) { + const { sectorSlug } = await params; const data = await GraphQL( sectorQueryDoc, {}, - { filters: { slug: params.sectorSlug } } + { filters: { slug: sectorSlug } } ); const sector = data?.sectors?.[0]; @@ -38,7 +39,7 @@ export async function generateMetadata({ openGraph: { type: 'article', locale: 'en_US', - url: `${process.env.NEXT_PUBLIC_PLATFORM_URL}/sectors/${params.sectorSlug}`, + url: `${process.env.NEXT_PUBLIC_PLATFORM_URL}/sectors/${sectorSlug}`, title: `${sector?.name} | Sector Data | CivicDataSpace`, description: sector?.description || @@ -52,12 +53,13 @@ export async function generateMetadata({ const SectorDetailsPage = async ({ params, }: { - params: { sectorSlug: string }; + params: Promise<{ sectorSlug: string }>; }) => { + const { sectorSlug } = await params; const data = await GraphQL( sectorQueryDoc, {}, - { filters: { slug: params.sectorSlug } } + { filters: { slug: sectorSlug } } ); const sector = data?.sectors?.[0]; diff --git a/app/[locale]/(user)/usecases/[useCaseSlug]/Dashboards.tsx b/app/[locale]/(user)/usecases/[useCaseSlug]/Dashboards.tsx index b8525066..056e45bc 100644 --- a/app/[locale]/(user)/usecases/[useCaseSlug]/Dashboards.tsx +++ b/app/[locale]/(user)/usecases/[useCaseSlug]/Dashboards.tsx @@ -18,16 +18,28 @@ const DashboardsList: any = graphql(` `); const Dashboards = () => { - const params = useParams(); + const params = useParams<{ useCaseSlug?: string }>(); + const useCaseSlug = params?.useCaseSlug; + + const usecaseId = + typeof useCaseSlug === 'string' ? Number.parseInt(useCaseSlug, 10) : NaN; + + const isValidId = !Number.isNaN(usecaseId); + const { data, isLoading } = useQuery<{ usecaseDashboards: any }>( - ['fetch_dashboardData', params.useCaseSlug], - () => GraphQL(DashboardsList, {}, { usecaseId: +params.useCaseSlug }), + ['fetch_dashboardData', usecaseId], + () => GraphQL(DashboardsList, {}, { usecaseId }), { refetchOnMount: true, refetchOnReconnect: true, + enabled: isValidId, } ); + if (!isValidId) { + return null; + } + return (
{isLoading ? ( @@ -51,7 +63,11 @@ const Dashboards = () => { className="flex h-[100px] w-full items-center rounded-4 bg-surfaceDefault p-5 shadow-card" target="_blank" > - + {dashboard.name} diff --git a/app/[locale]/(user)/usecases/[useCaseSlug]/UsecaseDetailsClient.tsx b/app/[locale]/(user)/usecases/[useCaseSlug]/UsecaseDetailsClient.tsx index 56f2692d..044e1699 100644 --- a/app/[locale]/(user)/usecases/[useCaseSlug]/UsecaseDetailsClient.tsx +++ b/app/[locale]/(user)/usecases/[useCaseSlug]/UsecaseDetailsClient.tsx @@ -1,17 +1,17 @@ 'use client'; +import { useEffect } from 'react'; import Image from 'next/image'; import Link from 'next/link'; import { useParams } from 'next/navigation'; -import { useEffect } from 'react'; import { graphql } from '@/gql'; import { TypeDataset, TypeUseCase } from '@/gql/generated/graphql'; +import { useAnalytics } from '@/hooks/use-analytics'; import { useQuery } from '@tanstack/react-query'; import { Card, Text } from 'opub-ui'; import { GraphQLPublic } from '@/lib/api'; import { formatDate, generateJsonLd } from '@/lib/utils'; -import { useAnalytics } from '@/hooks/use-analytics'; import BreadCrumbs from '@/components/BreadCrumbs'; import { Icons } from '@/components/icons'; import JsonLd from '@/components/JsonLd'; @@ -174,13 +174,13 @@ const UseCaseDetailClient = () => { } = useQuery<{ useCase: TypeUseCase }>( [`fetch_UsecaseDetails_${params.useCaseSlug}`], async () => { - const result = await GraphQLPublic( + const result = (await GraphQLPublic( UseCasedetails as any, {}, { pk: params.useCaseSlug, } - ) as { useCase: TypeUseCase }; + )) as { useCase: TypeUseCase }; return result; }, { @@ -195,7 +195,10 @@ const UseCaseDetailClient = () => { // Track usecase view when data is loaded useEffect(() => { if (UseCaseDetails?.useCase) { - trackUsecase(UseCaseDetails.useCase.id, UseCaseDetails.useCase.title || undefined); + trackUsecase( + UseCaseDetails.useCase.id, + UseCaseDetails.useCase.title || undefined + ); } }, [UseCaseDetails?.useCase, trackUsecase]); @@ -241,7 +244,8 @@ const UseCaseDetailClient = () => { Error Loading Use Case - {(error as any)?.message?.includes('401') || (error as any)?.message?.includes('403') + {(error as any)?.message?.includes('401') || + (error as any)?.message?.includes('403') ? 'You do not have permission to view this use case. Please log in or contact the administrator.' : 'Failed to load use case details. Please try again later.'} @@ -292,28 +296,31 @@ const UseCaseDetailClient = () => { iconColor={'warning'} metadataContent={[ { - icon: Icons.calendar, + icon: Icons.calendar as any, label: 'Date', value: formatDate(dataset.modified), }, { - icon: Icons.download, + icon: Icons.download as any, label: 'Download', value: dataset.downloadCount.toString(), }, { - icon: Icons.globe, + icon: Icons.globe as any, label: 'Geography', value: - dataset.geographies && dataset.geographies.length > 0 - ? dataset.geographies.map((geo: any) => geo.name).join(', ') + dataset.geographies && + dataset.geographies.length > 0 + ? dataset.geographies + .map((geo: any) => geo.name) + .join(', ') : '', }, ]} href={`/datasets/${dataset.id}`} footerContent={[ { - icon: `/Sectors/${dataset.sectors[0]?.name}.svg`, + icon: `/Sectors/${dataset.sectors[0]?.name}.svg` as any, label: 'Sectors', }, { diff --git a/app/[locale]/(user)/usecases/[useCaseSlug]/page.tsx b/app/[locale]/(user)/usecases/[useCaseSlug]/page.tsx index ed9223dc..6c87caf2 100644 --- a/app/[locale]/(user)/usecases/[useCaseSlug]/page.tsx +++ b/app/[locale]/(user)/usecases/[useCaseSlug]/page.tsx @@ -26,10 +26,11 @@ const UseCaseInfoQuery = graphql(` export async function generateMetadata({ params, }: { - params: { useCaseSlug: string }; + params: Promise<{ useCaseSlug: string }>; }): Promise { + const { useCaseSlug } = await params; try { - const data = await GraphQLPublic(UseCaseInfoQuery, {}, { pk: params.useCaseSlug }); + const data = await GraphQLPublic(UseCaseInfoQuery, {}, { pk: useCaseSlug }); const UseCase = data?.useCase; return generatePageMetadata({ @@ -41,7 +42,7 @@ export async function generateMetadata({ openGraph: { type: 'article', locale: 'en_US', - url: `${process.env.NEXT_PUBLIC_PLATFORM_URL}/usecases/${params.useCaseSlug}`, + url: `${process.env.NEXT_PUBLIC_PLATFORM_URL}/usecases/${useCaseSlug}`, title: `${UseCase?.title} | Sector Data | CivicDataSpace`, description: UseCase?.summary || @@ -59,7 +60,7 @@ export async function generateMetadata({ openGraph: { type: 'article', locale: 'en_US', - url: `${process.env.NEXT_PUBLIC_PLATFORM_URL}/usecases/${params.useCaseSlug}`, + url: `${process.env.NEXT_PUBLIC_PLATFORM_URL}/usecases/${useCaseSlug}`, title: `Use Case Details | CivicDataSpace`, description: `Explore open data and curated datasets in this use case.`, siteName: 'CivicDataSpace', diff --git a/app/[locale]/dashboard/[entityType]/[entitySlug]/admin/addUser.tsx b/app/[locale]/dashboard/[entityType]/[entitySlug]/admin/addUser.tsx index b3056dc6..dc5da2e5 100644 --- a/app/[locale]/dashboard/[entityType]/[entitySlug]/admin/addUser.tsx +++ b/app/[locale]/dashboard/[entityType]/[entitySlug]/admin/addUser.tsx @@ -265,9 +265,11 @@ const AddUser = ({ disabled={!formData.userId || !formData.roleId} onClick={() => { setIsOpen(false); - isEdit - ? updateMutate({ input: formData }) - : mutate({ input: formData }); + if (isEdit) { + updateMutate({ input: formData }); + } else { + mutate({ input: formData }); + } }} > {isEdit ? 'Update' : 'Add'} diff --git a/app/[locale]/dashboard/[entityType]/[entitySlug]/aimodels/edit/[id]/page.tsx b/app/[locale]/dashboard/[entityType]/[entitySlug]/aimodels/edit/[id]/page.tsx index 8998e2d5..24d728a9 100644 --- a/app/[locale]/dashboard/[entityType]/[entitySlug]/aimodels/edit/[id]/page.tsx +++ b/app/[locale]/dashboard/[entityType]/[entitySlug]/aimodels/edit/[id]/page.tsx @@ -1,12 +1,13 @@ import { redirect } from 'next/navigation'; -export default function AIModelEditPage({ +export default async function AIModelEditPage({ params, }: { - params: { entityType: string; entitySlug: string; id: string }; + params: Promise<{ entityType: string; entitySlug: string; id: string }>; }) { + const { entityType, entitySlug, id } = await params; // Redirect to the details page by default redirect( - `/dashboard/${params.entityType}/${params.entitySlug}/aimodels/edit/${params.id}/details` + `/dashboard/${entityType}/${entitySlug}/aimodels/edit/${id}/details` ); } diff --git a/app/[locale]/dashboard/[entityType]/[entitySlug]/aimodels/page.tsx b/app/[locale]/dashboard/[entityType]/[entitySlug]/aimodels/page.tsx index 4bee0a5f..d59aa7df 100644 --- a/app/[locale]/dashboard/[entityType]/[entitySlug]/aimodels/page.tsx +++ b/app/[locale]/dashboard/[entityType]/[entitySlug]/aimodels/page.tsx @@ -1,6 +1,6 @@ 'use client'; -import { useEffect } from 'react'; +import { use, useEffect } from 'react'; import { useRouter } from 'next/navigation'; import { graphql } from '@/gql'; import { useMutation, useQuery } from '@tanstack/react-query'; @@ -56,8 +56,12 @@ const createAIModel: any = graphql(` export default function AIModelsPage({ params, }: { - params: { entityType: string; entitySlug: string }; + params: Promise<{ entityType: string; entitySlug: string }>; }) { + const { entityType, entitySlug } = use(params) as { + entityType: string; + entitySlug: string; + }; const router = useRouter(); const [navigationTab, setNavigationTab] = useQueryState('tab', parseAsString); @@ -86,7 +90,7 @@ export default function AIModelsPage({ GraphQL( allAIModels, { - [params.entityType]: params.entitySlug, + [entityType]: entitySlug, }, { filters: { @@ -118,7 +122,7 @@ export default function AIModelsPage({ GraphQL( deleteAIModel, { - [params.entityType]: params.entitySlug, + [entityType]: entitySlug, }, { modelId: data.id } ), @@ -143,7 +147,7 @@ export default function AIModelsPage({ GraphQL( createAIModel, { - [params.entityType]: params.entitySlug, + [entityType]: entitySlug, }, { input: { @@ -159,7 +163,7 @@ export default function AIModelsPage({ onSuccess: (response: any) => { toast(`AI Model created successfully`); router.push( - `/dashboard/${params.entityType}/${params.entitySlug}/aimodels/edit/${response.createAiModel.data.id}/details` + `/dashboard/${entityType}/${entitySlug}/aimodels/edit/${response.createAiModel.data.id}/details` ); AllAIModels.refetch(); }, @@ -177,7 +181,7 @@ export default function AIModelsPage({ {row.original.displayName} @@ -277,7 +281,9 @@ export default function AIModelsPage({ ) : ( <>
-
+
= ({ icon={Icons.delete} color="interactive" onClick={() => { - row.original.typename === 'TypeResourceChart' - ? deleteResourceChartmutation.mutate({ - chartId: row.original.id, - }) - : deleteResourceChartImagemutation.mutate({ - resourceChartImageId: row.original.id, - }); + if (row.original.typename === 'TypeResourceChart') { + deleteResourceChartmutation.mutate({ + chartId: row.original.id, + }); + } else { + deleteResourceChartImagemutation.mutate({ + resourceChartImageId: row.original.id, + }); + } }} > Delete diff --git a/app/[locale]/dashboard/[entityType]/[entitySlug]/collaboratives/page.tsx b/app/[locale]/dashboard/[entityType]/[entitySlug]/collaboratives/page.tsx index b00b17a7..c80da93b 100644 --- a/app/[locale]/dashboard/[entityType]/[entitySlug]/collaboratives/page.tsx +++ b/app/[locale]/dashboard/[entityType]/[entitySlug]/collaboratives/page.tsx @@ -1,7 +1,7 @@ 'use client'; import { useEffect } from 'react'; -import { useRouter } from 'next/navigation'; +import { useParams, useRouter } from 'next/navigation'; import { graphql } from '@/gql'; import { useMutation, useQuery } from '@tanstack/react-query'; import { parseAsString, useQueryState } from 'next-usequerystate'; @@ -17,7 +17,10 @@ import { ActionBar } from '../dataset/components/action-bar'; import { Navigation } from '../dataset/components/navigate-org-datasets'; const allCollaboratives: any = graphql(` - query CollaborativesData($filters: CollaborativeFilter, $order: CollaborativeOrder) { + query CollaborativesData( + $filters: CollaborativeFilter + $order: CollaborativeOrder + ) { collaboratives(filters: $filters, order: $order) { title id @@ -58,50 +61,46 @@ const unPublishCollaborative: any = graphql(` } `); -export default function CollaborativePage({ - params, -}: { - params: { entityType: string; entitySlug: string }; -}) { +export default function CollaborativePage() { const router = useRouter(); + const params = useParams<{ entityType?: string; entitySlug?: string }>(); + const entityType = params?.entityType; + const entitySlug = params?.entitySlug; - const [navigationTab, setNavigationTab] = useQueryState('tab', parseAsString); + const isValidParams = + typeof entityType === 'string' && typeof entitySlug === 'string'; - let navigationOptions = [ - { - label: 'Drafts', - url: `drafts`, - selected: navigationTab === 'drafts', - }, - { - label: 'Published', - url: `published`, - selected: navigationTab === 'published', - }, - ]; + const ownerArgs: Record | null = isValidParams + ? { [entityType]: entitySlug } + : null; - const AllCollaboratives: { data: any; isLoading: boolean; refetch: any } = useQuery( - [`fetch_Collaboratives`], - () => - GraphQL( - allCollaboratives, - { - [params.entityType]: params.entitySlug, - }, - { + const [navigationTab, setNavigationTab] = useQueryState('tab', parseAsString); + + const AllCollaboratives: { data: any; isLoading: boolean; refetch: any } = + useQuery( + [ + 'fetch_Collaboratives', + entityType, + entitySlug, + navigationTab ?? 'drafts', + ], + () => + GraphQL(allCollaboratives, ownerArgs || {}, { filters: { status: navigationTab === 'published' ? 'PUBLISHED' : 'DRAFT', }, order: { modified: 'DESC' }, - } - ) - ); + }), + { enabled: isValidParams } + ); useEffect(() => { if (navigationTab === null || navigationTab === undefined) setNavigationTab('drafts'); - AllCollaboratives.refetch(); - }, [navigationTab]); + if (isValidParams) { + AllCollaboratives.refetch(); + } + }, [navigationTab, isValidParams]); const DeleteCollaborativeMutation: { mutate: any; @@ -110,17 +109,15 @@ export default function CollaborativePage({ } = useMutation( [`delete_Collaborative`], (data: { id: string }) => - GraphQL( - deleteCollaborative, - { - [params.entityType]: params.entitySlug, - }, - { collaborativeId: data.id } - ), + GraphQL(deleteCollaborative, ownerArgs || {}, { + collaborativeId: data.id, + }), { onSuccess: () => { toast(`Deleted Collaborative successfully`); - AllCollaboratives.refetch(); + if (isValidParams) { + AllCollaboratives.refetch(); + } }, onError: (err: any) => { toast('Error: ' + err.message.split(':')[0]); @@ -134,21 +131,16 @@ export default function CollaborativePage({ error: any; } = useMutation( [`create_Collaborative`], - () => - GraphQL( - AddCollaborative, - { - [params.entityType]: params.entitySlug, - }, - [] - ), + () => GraphQL(AddCollaborative, ownerArgs || {}, []), { onSuccess: (response: any) => { toast(`Collaborative created successfully`); - router.push( - `/dashboard/${params.entityType}/${params.entitySlug}/collaboratives/edit/${response.addCollaborative.id}/details` - ); - AllCollaboratives.refetch(); + if (isValidParams && entityType && entitySlug) { + router.push( + `/dashboard/${entityType}/${entitySlug}/collaboratives/edit/${response.addCollaborative.id}/details` + ); + AllCollaboratives.refetch(); + } }, onError: (err: any) => { toast('Error: ' + err.message.split(':')[0]); @@ -163,17 +155,15 @@ export default function CollaborativePage({ } = useMutation( [`unpublish_collaborative`], (data: { id: string }) => - GraphQL( - unPublishCollaborative, - { - [params.entityType]: params.entitySlug, - }, - { collaborativeId: data.id } - ), + GraphQL(unPublishCollaborative, ownerArgs || {}, { + collaborativeId: data.id, + }), { onSuccess: () => { toast(`Unpublished collaborative successfully`); - AllCollaboratives.refetch(); + if (isValidParams) { + AllCollaboratives.refetch(); + } }, onError: (err: any) => { toast('Error: ' + err.message.split(':')[0]); @@ -181,6 +171,23 @@ export default function CollaborativePage({ } ); + if (!isValidParams) { + return null; + } + + let navigationOptions = [ + { + label: 'Drafts', + url: `drafts`, + selected: navigationTab === 'drafts', + }, + { + label: 'Published', + url: `published`, + selected: navigationTab === 'published', + }, + ]; + const collaborativesListColumns = [ { accessorKey: 'title', @@ -197,7 +204,7 @@ export default function CollaborativePage({ {row.original.title} diff --git a/app/[locale]/dashboard/[entityType]/[entitySlug]/dataset/[id]/edit/components/EditMetadata.tsx b/app/[locale]/dashboard/[entityType]/[entitySlug]/dataset/[id]/edit/components/EditMetadata.tsx index 0abdf21b..9939e507 100644 --- a/app/[locale]/dashboard/[entityType]/[entitySlug]/dataset/[id]/edit/components/EditMetadata.tsx +++ b/app/[locale]/dashboard/[entityType]/[entitySlug]/dataset/[id]/edit/components/EditMetadata.tsx @@ -303,7 +303,7 @@ export function EditMetadata({ id }: { id: string }) { }; } - (dataset?.metadata || []).length > 0 && + if ((dataset?.metadata || []).length > 0) { (dataset?.metadata || []).map((field) => { if ( field.metadataItem.dataType === 'MULTISELECT' && @@ -321,6 +321,7 @@ export function EditMetadata({ id }: { id: string }) { defaultVal[field.metadataItem.id] = field.value; } }); + } defaultVal['description'] = dataset?.description || ''; diff --git a/app/[locale]/dashboard/[entityType]/[entitySlug]/dataset/[id]/edit/layout.tsx b/app/[locale]/dashboard/[entityType]/[entitySlug]/dataset/[id]/edit/layout.tsx index 2585c303..cb22f34a 100644 --- a/app/[locale]/dashboard/[entityType]/[entitySlug]/dataset/[id]/edit/layout.tsx +++ b/app/[locale]/dashboard/[entityType]/[entitySlug]/dataset/[id]/edit/layout.tsx @@ -5,16 +5,17 @@ import { DatasetEditStatusProvider } from './context'; interface DashboardLayoutProps { children?: React.ReactNode; - params: { id: string }; + params: Promise<{ id: string }>; } export default async function Layout({ children, params, }: DashboardLayoutProps) { + const resolvedParams = await params; return ( - {children} + {children} ); } diff --git a/app/[locale]/dashboard/[entityType]/[entitySlug]/dataset/[id]/edit/metadata/page.tsx b/app/[locale]/dashboard/[entityType]/[entitySlug]/dataset/[id]/edit/metadata/page.tsx index e0e8ea5d..40884c51 100644 --- a/app/[locale]/dashboard/[entityType]/[entitySlug]/dataset/[id]/edit/metadata/page.tsx +++ b/app/[locale]/dashboard/[entityType]/[entitySlug]/dataset/[id]/edit/metadata/page.tsx @@ -4,12 +4,13 @@ import styles from '../edit.module.scss'; export default async function Page({ params, }: { - params: { id: string; entitySlug: string }; + params: Promise<{ id: string; entitySlug: string }>; }) { + const { id } = await params; return ( //
- +
//
); diff --git a/app/[locale]/dashboard/[entityType]/[entitySlug]/dataset/[id]/edit/page.tsx b/app/[locale]/dashboard/[entityType]/[entitySlug]/dataset/[id]/edit/page.tsx index 80fffea2..fd6e0a16 100644 --- a/app/[locale]/dashboard/[entityType]/[entitySlug]/dataset/[id]/edit/page.tsx +++ b/app/[locale]/dashboard/[entityType]/[entitySlug]/dataset/[id]/edit/page.tsx @@ -5,16 +5,21 @@ import styles from './edit.module.scss'; import { EditPage } from './page-layout'; -export default async function Page({ params }: { params: { id: string } }) { +export default async function Page({ + params, +}: { + params: Promise<{ id: string }>; +}) { + const { id } = await params; const queryClient = getQueryClient(); // eslint-disable-next-line @typescript-eslint/no-unused-vars const dehydratedState = dehydrate(queryClient); return ( // -
- -
+
+ +
//
); } diff --git a/app/[locale]/dashboard/[entityType]/[entitySlug]/dataset/[id]/edit/resources/components/PreviewData.tsx b/app/[locale]/dashboard/[entityType]/[entitySlug]/dataset/[id]/edit/resources/components/PreviewData.tsx index 5e0e7ebb..628ff77b 100644 --- a/app/[locale]/dashboard/[entityType]/[entitySlug]/dataset/[id]/edit/resources/components/PreviewData.tsx +++ b/app/[locale]/dashboard/[entityType]/[entitySlug]/dataset/[id]/edit/resources/components/PreviewData.tsx @@ -15,7 +15,6 @@ const PreviewData = ({ previewData }: EditProps) => { cell: ({ cell }: any) => { const value = cell.getValue(); return {value !== null ? value?.toString() : 'N/A'}; - ``; }, })) || []; diff --git a/app/[locale]/dashboard/[entityType]/[entitySlug]/dataset/[id]/edit/resources/components/ResourceHeader.tsx b/app/[locale]/dashboard/[entityType]/[entitySlug]/dataset/[id]/edit/resources/components/ResourceHeader.tsx index 98ca9216..c057c616 100644 --- a/app/[locale]/dashboard/[entityType]/[entitySlug]/dataset/[id]/edit/resources/components/ResourceHeader.tsx +++ b/app/[locale]/dashboard/[entityType]/[entitySlug]/dataset/[id]/edit/resources/components/ResourceHeader.tsx @@ -63,7 +63,7 @@ const ResourceHeader = ({ allowMultiple={true} onDrop={dropZone} > - {uploadedFile} + {uploadedFile as any} {file.length === 0 && ( ; }) { + const resolvedParams = await params; const queryClient = getQueryClient(); // eslint-disable-next-line @typescript-eslint/no-unused-vars const dehydratedState = dehydrate(queryClient); @@ -16,7 +17,7 @@ export default async function Page({ return ( //
- +
//
); diff --git a/app/[locale]/dashboard/[entityType]/[entitySlug]/dataset/[id]/edit/review/page.tsx b/app/[locale]/dashboard/[entityType]/[entitySlug]/dataset/[id]/edit/review/page.tsx index c1ba0adf..c99a2e86 100644 --- a/app/[locale]/dashboard/[entityType]/[entitySlug]/dataset/[id]/edit/review/page.tsx +++ b/app/[locale]/dashboard/[entityType]/[entitySlug]/dataset/[id]/edit/review/page.tsx @@ -1,6 +1,11 @@ import { InProgress } from '@/components/in-progress'; -export default async function Page() { +export default async function Page({ + params, +}: { + params: Promise<{ id: string }>; +}) { + await params; return ( <> diff --git a/app/[locale]/dashboard/[entityType]/[entitySlug]/dataset/[id]/page.tsx b/app/[locale]/dashboard/[entityType]/[entitySlug]/dataset/[id]/page.tsx index 0646b554..e64d505a 100644 --- a/app/[locale]/dashboard/[entityType]/[entitySlug]/dataset/[id]/page.tsx +++ b/app/[locale]/dashboard/[entityType]/[entitySlug]/dataset/[id]/page.tsx @@ -1,12 +1,32 @@ 'use client'; import React from 'react'; -import { notFound } from 'next/navigation'; +import { notFound, useParams } from 'next/navigation'; + +// import { usePRouter } from '@/hooks/use-prouter'; +// import { Divider, Icon, Tab, TabList, TabPanel, Tabs, Text } from 'opub-ui'; import { testDataset } from '@/config/dashboard'; -export default function Page({ params }: { params: { id: string } }) { - const data = testDataset[params.id]; +// import { Icons } from '@/components/icons'; +// import { InProgress } from '@/components/in-progress'; +// import { ActionBar } from '../components/action-bar'; + +export default function Page() { + const params = useParams<{ id: string }>(); + const datasetId = params?.id; + // const router = usePRouter(); + // React.useEffect(() => { + // router.prefetch(`/dashboard/dataset/${params.id}/edit`); + // }, []); + + // get demo data + + if (typeof datasetId !== 'string') { + notFound(); + } + + const data = testDataset[datasetId]; if (!data) { notFound(); } diff --git a/app/[locale]/dashboard/[entityType]/[entitySlug]/dataset/components/content.tsx b/app/[locale]/dashboard/[entityType]/[entitySlug]/dataset/components/content.tsx index 8b949079..ce3e5103 100644 --- a/app/[locale]/dashboard/[entityType]/[entitySlug]/dataset/components/content.tsx +++ b/app/[locale]/dashboard/[entityType]/[entitySlug]/dataset/components/content.tsx @@ -1,6 +1,6 @@ 'use client'; -import { useRouter } from 'next/navigation'; +import { useParams, useRouter } from 'next/navigation'; import { graphql } from '@/gql'; import { useMutation } from '@tanstack/react-query'; import { Button, Icon, Text, toast } from 'opub-ui'; @@ -26,38 +26,38 @@ const createDatasetMutationDoc: any = graphql(` } } `); -export const Content = ({ - params, -}: { - params: { entityType: string; entitySlug: string }; -}) => { +export const Content = () => { const router = useRouter(); + const params = useParams<{ entityType?: string; entitySlug?: string }>(); + const entityType = params?.entityType; + const entitySlug = params?.entitySlug; + + const isValidParams = + typeof entityType === 'string' && typeof entitySlug === 'string'; + + const ownerArgs: Record | null = isValidParams + ? { [entityType]: entitySlug } + : null; const CreateDatasetMutation: { mutate: any; isLoading: boolean; error: any } = - useMutation( - () => - GraphQL( - createDatasetMutationDoc, - { - [params.entityType]: params.entitySlug, - }, - [] - ), - { - onSuccess: (data: any) => { - if (data.addDataset.success) { - toast('Dataset created successfully!'); + useMutation(() => GraphQL(createDatasetMutationDoc, ownerArgs || {}, []), { + onSuccess: (data: any) => { + if (data.addDataset.success) { + toast('Dataset created successfully!'); + if (isValidParams && entityType && entitySlug) { router.push( - `/dashboard/${params.entityType}/${params.entitySlug}/dataset/${data?.addDataset?.data?.id}/edit/metadata` - ); - } else { - toast( - 'Error: ' + data.addDataset.errors.fieldErrors[0].messages[0] + `/dashboard/${entityType}/${entitySlug}/dataset/${data?.addDataset?.data?.id}/edit/metadata` ); } - }, - } - ); + } else { + toast('Error: ' + data.addDataset.errors.fieldErrors[0].messages[0]); + } + }, + }); + + if (!isValidParams) { + return null; + } return (
diff --git a/app/[locale]/dashboard/[entityType]/[entitySlug]/dataset/components/dataset-form.tsx b/app/[locale]/dashboard/[entityType]/[entitySlug]/dataset/components/dataset-form.tsx index f3b5d443..97454f09 100644 --- a/app/[locale]/dashboard/[entityType]/[entitySlug]/dataset/components/dataset-form.tsx +++ b/app/[locale]/dashboard/[entityType]/[entitySlug]/dataset/components/dataset-form.tsx @@ -18,7 +18,7 @@ export function DatasetForm({ }: DatasetFormProps) { return (
- {children} + {children as any} diff --git a/app/[locale]/dashboard/[entityType]/[entitySlug]/dataset/new/components/new-dataset.tsx b/app/[locale]/dashboard/[entityType]/[entitySlug]/dataset/new/components/new-dataset.tsx index 1e54f02c..6130171c 100644 --- a/app/[locale]/dashboard/[entityType]/[entitySlug]/dataset/new/components/new-dataset.tsx +++ b/app/[locale]/dashboard/[entityType]/[entitySlug]/dataset/new/components/new-dataset.tsx @@ -43,8 +43,7 @@ export function CreateDataset({ return ( { - mutatePatch && - defaultVal && + if (mutatePatch && defaultVal) { mutatePatch({ dataset_data: { title: value.title, @@ -52,8 +51,9 @@ export function CreateDataset({ id: defaultVal.id, }, }); + } - mutate && + if (mutate) { mutate({ dataset_data: { title: value.title, @@ -61,6 +61,7 @@ export function CreateDataset({ dataset_type: value.type, }, }); + } }} formOptions={{ defaultValues: defaultValue }} submitRef={submitRef} diff --git a/app/[locale]/dashboard/[entityType]/[entitySlug]/dataset/page-layout.tsx b/app/[locale]/dashboard/[entityType]/[entitySlug]/dataset/page-layout.tsx index 87139153..feb8203f 100644 --- a/app/[locale]/dashboard/[entityType]/[entitySlug]/dataset/page-layout.tsx +++ b/app/[locale]/dashboard/[entityType]/[entitySlug]/dataset/page-layout.tsx @@ -28,34 +28,35 @@ const createDatasetMutationDoc: any = graphql(` `); export const Page = () => { - const params = useParams<{ - entityType: string; - entitySlug: string; - }>(); - const router = useRouter(); + const params = useParams<{ entityType?: string; entitySlug?: string }>(); + const entityType = params?.entityType; + const entitySlug = params?.entitySlug; + + const isValidParams = + typeof entityType === 'string' && typeof entitySlug === 'string'; + + const ownerArgs: Record | null = isValidParams + ? { [entityType]: entitySlug } + : null; + const router = useRouter(); const queryClient = useQueryClient(); const { mutate, isLoading } = useMutation( - () => - GraphQL( - createDatasetMutationDoc, - { - [params.entityType]: params.entitySlug, - }, - [] - ), + () => GraphQL(createDatasetMutationDoc, ownerArgs || {}, []), { onSuccess: (data: any) => { if (data.addDataset.success) { toast('Dataset created successfully!'); - queryClient.invalidateQueries({ - queryKey: [`create_dataset_${params.entityType}`], - }); + if (isValidParams && entityType) { + queryClient.invalidateQueries({ + queryKey: [`create_dataset_${entityType}`], + }); - router.push( - `/dashboard/${params.entityType}/${params.entitySlug}/dataset/${data?.addDataset?.data?.id}/edit/metadata` - ); + router.push( + `/dashboard/${entityType}/${entitySlug}/dataset/${data?.addDataset?.data?.id}/edit/metadata` + ); + } } else { toast('Error: ' + data.addDataset.errors.fieldErrors[0].messages[0]); } @@ -63,6 +64,10 @@ export const Page = () => { } ); + if (!isValidParams) { + return null; + } + // React.useEffect(() => { // router.prefetch('/dashboard/dataset/new'); // }, []); @@ -80,7 +85,7 @@ export const Page = () => { isLoading={isLoading} /> - + ); }; diff --git a/app/[locale]/dashboard/[entityType]/[entitySlug]/dataset/page.tsx b/app/[locale]/dashboard/[entityType]/[entitySlug]/dataset/page.tsx index c6f99c91..c717633f 100644 --- a/app/[locale]/dashboard/[entityType]/[entitySlug]/dataset/page.tsx +++ b/app/[locale]/dashboard/[entityType]/[entitySlug]/dataset/page.tsx @@ -1,21 +1,21 @@ 'use client'; import { useEffect } from 'react'; -import { useRouter } from 'next/navigation'; +import { useParams, useRouter } from 'next/navigation'; import { graphql } from '@/gql'; import { useMutation, useQuery } from '@tanstack/react-query'; import { parseAsString, useQueryState } from 'next-usequerystate'; import { Button, DataTable, IconButton, Text, toast } from 'opub-ui'; import { GraphQL } from '@/lib/api'; +import { formatDate } from '@/lib/utils'; import { Icons } from '@/components/icons'; import { LinkButton } from '@/components/Link'; import { Loading } from '@/components/loading'; +import { useTourTrigger } from '@/hooks/use-tour-trigger'; import { ActionBar } from './components/action-bar'; import { Content } from './components/content'; import { Navigation } from './components/navigate-org-datasets'; -import { formatDate } from '@/lib/utils'; -import { useTourTrigger } from '@/hooks/use-tour-trigger'; const allDatasetsQueryDoc: any = graphql(` query allDatasetsQuery($filters: DatasetFilter, $order: DatasetOrder) { @@ -65,51 +65,47 @@ const unPublishDataset: any = graphql(` } `); -export default function DatasetPage({ - params, -}: { - params: { entityType: string; entitySlug: string }; -}) { +export default function DatasetPage() { useTourTrigger(true, 1500); - const router = useRouter(); + const params = useParams<{ entityType?: string; entitySlug?: string }>(); + const entityType = params?.entityType; + const entitySlug = params?.entitySlug; - const [navigationTab, setNavigationTab] = useQueryState('tab', parseAsString); + const isValidParams = + typeof entityType === 'string' && typeof entitySlug === 'string'; - let navigationOptions = [ - { - label: 'Drafts', - url: `drafts`, - selected: navigationTab === 'drafts', - }, - { - label: 'Published', - url: `published`, - selected: navigationTab === 'published', - }, - ]; + const ownerArgs: Record | null = isValidParams + ? { [entityType]: entitySlug } + : null; + + const [navigationTab, setNavigationTab] = useQueryState('tab', parseAsString); const AllDatasetsQuery: { data: any; isLoading: boolean; refetch: any } = - useQuery([`fetch_datasets_org_dashboard`], () => - GraphQL( - allDatasetsQueryDoc, - { - [params.entityType]: params.entitySlug, - }, - { + useQuery( + [ + `fetch_datasets_org_dashboard`, + entityType, + entitySlug, + navigationTab ?? 'drafts', + ], + () => + GraphQL(allDatasetsQueryDoc, ownerArgs || {}, { filters: { status: navigationTab === 'published' ? 'PUBLISHED' : 'DRAFT', }, order: { modified: 'DESC' }, - } - ) + }), + { enabled: isValidParams } ); useEffect(() => { if (navigationTab === null || navigationTab === undefined) setNavigationTab('drafts'); - AllDatasetsQuery.refetch(); - }, [navigationTab]); + if (isValidParams) { + AllDatasetsQuery.refetch(); + } + }, [navigationTab, isValidParams]); const DeleteDatasetMutation: { mutate: any; @@ -118,17 +114,15 @@ export default function DatasetPage({ } = useMutation( [`delete_dataset`], (data: { datasetId: string }) => - GraphQL( - deleteDatasetMutationDoc, - { - [params.entityType]: params.entitySlug, - }, - { datasetId: data.datasetId } - ), + GraphQL(deleteDatasetMutationDoc, ownerArgs || {}, { + datasetId: data.datasetId, + }), { onSuccess: () => { toast(`Deleted dataset successfully`); - AllDatasetsQuery.refetch(); + if (isValidParams) { + AllDatasetsQuery.refetch(); + } }, onError: (err: any) => { toast('Error: ' + err.message.split(':')[0]); @@ -136,31 +130,20 @@ export default function DatasetPage({ } ); const CreateDatasetMutation: { mutate: any; isLoading: boolean; error: any } = - useMutation( - () => - GraphQL( - createDatasetMutationDoc, - { - [params.entityType]: params.entitySlug, - }, - [] - ), - { - onSuccess: (data: any) => { - if (data.addDataset.success) { - toast('Dataset created successfully!'); - + useMutation(() => GraphQL(createDatasetMutationDoc, ownerArgs || {}, []), { + onSuccess: (data: any) => { + if (data.addDataset.success) { + toast('Dataset created successfully!'); + if (isValidParams && entityType && entitySlug) { router.push( - `/dashboard/${params.entityType}/${params.entitySlug}/dataset/${data?.addDataset?.data?.id}/edit/metadata` - ); - } else { - toast( - 'Error: ' + data.addDataset.errors.fieldErrors[0].messages[0] + `/dashboard/${entityType}/${entitySlug}/dataset/${data?.addDataset?.data?.id}/edit/metadata` ); } - }, - } - ); + } else { + toast('Error: ' + data.addDataset.errors.fieldErrors[0].messages[0]); + } + }, + }); const UnpublishDatasetMutation: { mutate: any; isLoading: boolean; @@ -168,17 +151,13 @@ export default function DatasetPage({ } = useMutation( [`unpublish_dataset`], (data: { datasetId: string }) => - GraphQL( - unPublishDataset, - { - [params.entityType]: params.entitySlug, - }, - { datasetId: data.datasetId } - ), + GraphQL(unPublishDataset, ownerArgs || {}, { datasetId: data.datasetId }), { onSuccess: () => { toast(`Unpublished dataset successfully`); - AllDatasetsQuery.refetch(); + if (isValidParams) { + AllDatasetsQuery.refetch(); + } }, onError: (err: any) => { toast('Error: ' + err.message.split(':')[0]); @@ -186,6 +165,23 @@ export default function DatasetPage({ } ); + if (!isValidParams) { + return null; + } + + let navigationOptions = [ + { + label: 'Drafts', + url: `drafts`, + selected: navigationTab === 'drafts', + }, + { + label: 'Published', + url: `published`, + selected: navigationTab === 'published', + }, + ]; + const datasetsListColumns = [ { accessorKey: 'title', @@ -197,7 +193,7 @@ export default function DatasetPage({ {row.original.title} @@ -282,7 +278,7 @@ export default function DatasetPage({ ) : AllDatasetsQuery.isLoading ? ( ) : ( - + )} {/* */} diff --git a/app/[locale]/dashboard/[entityType]/[entitySlug]/page.tsx b/app/[locale]/dashboard/[entityType]/[entitySlug]/page.tsx index e01da9e7..83f9428c 100644 --- a/app/[locale]/dashboard/[entityType]/[entitySlug]/page.tsx +++ b/app/[locale]/dashboard/[entityType]/[entitySlug]/page.tsx @@ -1,18 +1,10 @@ -'use client'; +import { redirect } from 'next/navigation'; -import { useEffect } from 'react'; -import { useRouter } from 'next/navigation'; - -export default function Page({ +export default async function Page({ params, }: { - params: { entityType: string; entitySlug: string }; + params: Promise<{ entityType: string; entitySlug: string }>; }) { - const router = useRouter(); - - useEffect(() => { - router.push(`/dashboard/${params.entityType}/${params.entitySlug}/dataset?tab=drafts`); - }, [params, router]); - - return null; // prevent rendering anything before redirect + const { entityType, entitySlug } = await params; + redirect(`/dashboard/${entityType}/${entitySlug}/dataset?tab=drafts`); } diff --git a/app/[locale]/dashboard/[entityType]/[entitySlug]/usecases/edit/[id]/dashboards/page.tsx b/app/[locale]/dashboard/[entityType]/[entitySlug]/usecases/edit/[id]/dashboards/page.tsx index 0aed444a..6105375a 100644 --- a/app/[locale]/dashboard/[entityType]/[entitySlug]/usecases/edit/[id]/dashboards/page.tsx +++ b/app/[locale]/dashboard/[entityType]/[entitySlug]/usecases/edit/[id]/dashboards/page.tsx @@ -2,6 +2,7 @@ import { useEffect, useState } from 'react'; import Link from 'next/link'; +import { useParams } from 'next/navigation'; import { graphql } from '@/gql'; import { useMutation, useQuery } from '@tanstack/react-query'; import { Button, Icon, Text, TextField, toast } from 'opub-ui'; @@ -73,8 +74,26 @@ const deleteDashboard: any = graphql(` } `); -const Dashboard = ({ params }: { params: { entityType: string; entitySlug: string; id: string } }) => { - const usecaseId = parseInt(params.id); +const Dashboard = () => { + const params = useParams<{ entityType?: string; entitySlug?: string; id?: string }>(); + const entityType = params?.entityType; + const entitySlug = params?.entitySlug; + const idParam = params?.id; + + const isValidParams = + typeof entityType === 'string' && + typeof entitySlug === 'string' && + typeof idParam === 'string'; + + const usecaseId = isValidParams && idParam + ? Number.parseInt(idParam, 10) + : NaN; + + const isValidId = !Number.isNaN(usecaseId) && isValidParams; + + const ownerArgs: Record | null = isValidParams + ? { [entityType]: entitySlug } + : null; const [dashboards, setDashboards] = useState< Array<{ id: string; name: string; link: string }> @@ -83,10 +102,11 @@ const Dashboard = ({ params }: { params: { entityType: string; entitySlug: strin useQuery( ['fetch_dashboardData', usecaseId], - () => GraphQL(dashboardList, { [params.entityType]: params.entitySlug }, { usecaseId }), + () => GraphQL(dashboardList, ownerArgs || {}, { usecaseId }), { refetchOnMount: true, refetchOnReconnect: true, + enabled: isValidId, onSuccess: (res: any) => { setDashboards(res.usecaseDashboards || []); setPreviousState( @@ -100,7 +120,7 @@ const Dashboard = ({ params }: { params: { entityType: string; entitySlug: strin const { mutate: addDashboard, isLoading: addLoading } = useMutation( ({ usecaseId }: { usecaseId: number }) => - GraphQL(AddDashboard, { [params.entityType]: params.entitySlug }, { usecaseId }), + GraphQL(AddDashboard, ownerArgs || {}, { usecaseId }), { onSuccess: (res: any) => { const newDashboard = res.addUsecaseDashboard.data; @@ -116,7 +136,7 @@ const Dashboard = ({ params }: { params: { entityType: string; entitySlug: strin ); const { mutate: saveDashboard, isLoading: saveLoading } = useMutation( ({ id, name, link }: { id: string; name: string; link: string }) => - GraphQL(updateDashboard, { [params.entityType]: params.entitySlug }, { id, name, link }), + GraphQL(updateDashboard, ownerArgs || {}, { id, name, link }), { onSuccess: ({ updateUsecaseDashboard }: any) => { toast.success('Changes saved'); @@ -132,7 +152,7 @@ const Dashboard = ({ params }: { params: { entityType: string; entitySlug: strin ); const { mutate: removeDashboard, isLoading: deleteLoading } = useMutation( - (id: number) => GraphQL(deleteDashboard, { [params.entityType]: params.entitySlug }, { id }), + (id: number) => GraphQL(deleteDashboard, ownerArgs || {}, { id }), { onSuccess: (_, id) => { setDashboards((prev) => prev.filter((d) => d.id !== id.toString())); @@ -144,6 +164,18 @@ const Dashboard = ({ params }: { params: { entityType: string; entitySlug: strin } ); + const { setStatus } = useEditStatus(); + + useEffect(() => { + setStatus( + saveLoading || addLoading || deleteLoading ? 'loading' : 'success' + ); // update based on mutation state + }, [saveLoading, addLoading, deleteLoading, setStatus]); + + if (!isValidId) { + return null; + } + const handleChange = (id: string, field: 'name' | 'link', value: string) => { setDashboards((prev) => prev.map((d) => (d.id === id ? { ...d, [field]: value } : d)) @@ -173,14 +205,6 @@ const Dashboard = ({ params }: { params: { entityType: string; entitySlug: strin removeDashboard(Number(id)); }; - const { setStatus } = useEditStatus(); - - useEffect(() => { - setStatus( - saveLoading || addLoading || deleteLoading ? 'loading' : 'success' - ); // update based on mutation state - }, [saveLoading, addLoading, deleteLoading]); - return (
Dashboard diff --git a/app/[locale]/dashboard/[entityType]/[entitySlug]/usecases/page.tsx b/app/[locale]/dashboard/[entityType]/[entitySlug]/usecases/page.tsx index 28e60d4c..d5c91837 100644 --- a/app/[locale]/dashboard/[entityType]/[entitySlug]/usecases/page.tsx +++ b/app/[locale]/dashboard/[entityType]/[entitySlug]/usecases/page.tsx @@ -1,7 +1,7 @@ 'use client'; import { useEffect } from 'react'; -import { useRouter } from 'next/navigation'; +import { useParams, useRouter } from 'next/navigation'; import { graphql } from '@/gql'; import { useMutation, useQuery } from '@tanstack/react-query'; import { parseAsString, useQueryState } from 'next-usequerystate'; @@ -58,50 +58,44 @@ const unPublishUseCase: any = graphql(` } `); -export default function DatasetPage({ - params, -}: { - params: { entityType: string; entitySlug: string }; -}) { +export default function DatasetPage() { const router = useRouter(); + const params = useParams<{ entityType?: string; entitySlug?: string }>(); + const entityType = params?.entityType; + const entitySlug = params?.entitySlug; - const [navigationTab, setNavigationTab] = useQueryState('tab', parseAsString); + const isValidParams = + typeof entityType === 'string' && typeof entitySlug === 'string'; - let navigationOptions = [ - { - label: 'Drafts', - url: `drafts`, - selected: navigationTab === 'drafts', - }, - { - label: 'Published', - url: `published`, - selected: navigationTab === 'published', - }, - ]; + const ownerArgs: Record | null = isValidParams + ? { [entityType]: entitySlug } + : null; + + const [navigationTab, setNavigationTab] = useQueryState('tab', parseAsString); const AllUseCases: { data: any; isLoading: boolean; refetch: any } = useQuery( - [`fetch_UseCases`], + [`fetch_UseCases`, entityType, entitySlug, navigationTab ?? 'drafts'], () => GraphQL( allUseCases, - { - [params.entityType]: params.entitySlug, - }, + ownerArgs || {}, { filters: { status: navigationTab === 'published' ? 'PUBLISHED' : 'DRAFT', }, order: { modified: 'DESC' }, } - ) + ), + { enabled: isValidParams } ); useEffect(() => { if (navigationTab === null || navigationTab === undefined) setNavigationTab('drafts'); - AllUseCases.refetch(); - }, [navigationTab]); + if (isValidParams) { + AllUseCases.refetch(); + } + }, [navigationTab, isValidParams]); const DeleteUseCaseMutation: { mutate: any; @@ -112,15 +106,15 @@ export default function DatasetPage({ (data: { id: string }) => GraphQL( deleteUseCase, - { - [params.entityType]: params.entitySlug, - }, + ownerArgs || {}, { useCaseId: data.id } ), { onSuccess: () => { toast(`Deleted UseCase successfully`); - AllUseCases.refetch(); + if (isValidParams) { + AllUseCases.refetch(); + } }, onError: (err: any) => { toast('Error: ' + err.message.split(':')[0]); @@ -134,21 +128,16 @@ export default function DatasetPage({ error: any; } = useMutation( [`delete_Usecase`], - () => - GraphQL( - AddUseCase, - { - [params.entityType]: params.entitySlug, - }, - [] - ), + () => GraphQL(AddUseCase, ownerArgs || {}, []), { onSuccess: (response: any) => { toast(`UseCase created successfully`); - router.push( - `/dashboard/${params.entityType}/${params.entitySlug}/usecases/edit/${response.addUseCase.id}/details` - ); - AllUseCases.refetch(); + if (isValidParams && entityType && entitySlug) { + router.push( + `/dashboard/${entityType}/${entitySlug}/usecases/edit/${response.addUseCase.id}/details` + ); + AllUseCases.refetch(); + } }, onError: (err: any) => { toast('Error: ' + err.message.split(':')[0]); @@ -164,21 +153,38 @@ export default function DatasetPage({ (data: { id: string }) => GraphQL( unPublishUseCase, - { - [params.entityType]: params.entitySlug, - }, + ownerArgs || {}, { useCaseId: data.id } ), { onSuccess: () => { toast(`Unpublished usecase successfully`); - AllUseCases.refetch(); + if (isValidParams) { + AllUseCases.refetch(); + } }, onError: (err: any) => { toast('Error: ' + err.message.split(':')[0]); }, } ); + + if (!isValidParams) { + return null; + } + + let navigationOptions = [ + { + label: 'Drafts', + url: `drafts`, + selected: navigationTab === 'drafts', + }, + { + label: 'Published', + url: `published`, + selected: navigationTab === 'published', + }, + ]; const datasetsListColumns = [ { accessorKey: 'title', @@ -195,7 +201,7 @@ export default function DatasetPage({ {row.original.title} diff --git a/app/[locale]/dashboard/components/main-nav.tsx b/app/[locale]/dashboard/components/main-nav.tsx index 5a32f3d4..41e64edd 100644 --- a/app/[locale]/dashboard/components/main-nav.tsx +++ b/app/[locale]/dashboard/components/main-nav.tsx @@ -129,7 +129,7 @@ export function MainNav({ hideSearch = false }) { } }; return ( -