diff --git a/.env.local.example b/.env.local.example index fbf5658f..7381629e 100644 --- a/.env.local.example +++ b/.env.local.example @@ -25,7 +25,18 @@ SENTRY_DSN_URL=https://sentrydsnurl NEXT_PUBLIC_SENTRY_DSN_URL=https://sentrydsnurl SENTRY_ORG_NAME='orgname' SENTRY_PROJECT_NAME='projectname' -NEXT_PUBLIC_PLATFORM_URL = 'https://platformurl' +NEXT_PUBLIC_PLATFORM_URL=https://platformurl + +# Domain used for slug subdomains (recommended to set explicitly) +# Local : localhost +# Dev : dev.civicdataspace.in +# Prod : civicdataspace.in +NEXT_PUBLIC_PLATFORM_DOMAIN=platformdomain + +# Optional: set protocol/port explicitly for slug subdomain URLs +NEXT_PUBLIC_PLATFORM_PROTOCOL=https +NEXT_PUBLIC_PLATFORM_PORT=3000 + # Google Analytics NEXT_PUBLIC_GA_ID='G-XXXXXXXXXX' diff --git a/.github/workflows/deploy-Dataspace.yml b/.github/workflows/deploy-Dataspace.yml index 2575604e..65985553 100644 --- a/.github/workflows/deploy-Dataspace.yml +++ b/.github/workflows/deploy-Dataspace.yml @@ -26,6 +26,8 @@ jobs: NEXT_PUBLIC_ENABLE_ACCESSMODEL: ${{ vars.NEXT_PUBLIC_ENABLE_ACCESSMODEL }} NEXT_PUBLIC_ANALYTICS_URL: ${{ vars.NEXT_PUBLIC_ANALYTICS_URL }} NEXT_PUBLIC_PLATFORM_URL: ${{ vars.NEXT_PUBLIC_PLATFORM_URL }} + NEXT_PUBLIC_PLATFORM_PROTOCOL: ${{ vars.NEXT_PUBLIC_PLATFORM_PROTOCOL }} + NEXT_PUBLIC_PLATFORM_DOMAIN: ${{ vars.NEXT_PUBLIC_PLATFORM_DOMAIN }} steps: - name: Checkout code diff --git a/app/[locale]/(user)/collaboratives/CollaborativesListingClient.tsx b/app/[locale]/(user)/collaboratives/CollaborativesListingClient.tsx index 3e5c4904..f7cefb3a 100644 --- a/app/[locale]/(user)/collaboratives/CollaborativesListingClient.tsx +++ b/app/[locale]/(user)/collaboratives/CollaborativesListingClient.tsx @@ -8,6 +8,7 @@ import { useQuery } from '@tanstack/react-query'; import { Button, Card, Icon, SearchInput, Select, Text } from 'opub-ui'; import { GraphQLPublic } from '@/lib/api'; +import { getCollaborativeDetailUrl } from '@/lib/collaborativesRouting'; import { cn, formatDate, generateJsonLd } from '@/lib/utils'; import BreadCrumbs from '@/components/BreadCrumbs'; import { Icons } from '@/components/icons'; @@ -97,7 +98,7 @@ const CollaborativesListingClient = () => { try { // @ts-expect-error - Query has no variables const result = await GraphQLPublic(PublishedCollaboratives as any, {}); - console.log('Collaboratives result:', result); + // console.log('Collaboratives result:', result); return result as { publishedCollaboratives: TypeCollaborative[] }; } catch (err) { console.error('Error fetching collaboratives:', err); @@ -336,7 +337,7 @@ const CollaborativesListingClient = () => { stroke: 1.2, }, ]} - href={`/collaboratives/${collaborative.slug}`} + href={getCollaborativeDetailUrl(collaborative.slug)} leftFooterChips={[ { icon: collaborative.sectors?.[0]?.name diff --git a/app/[locale]/(user)/collaboratives/[collaborativeSlug]/CollaborativeDetailsClient.tsx b/app/[locale]/(user)/collaboratives/[collaborativeSlug]/CollaborativeDetailsClient.tsx index 5112b762..6e1665de 100644 --- a/app/[locale]/(user)/collaboratives/[collaborativeSlug]/CollaborativeDetailsClient.tsx +++ b/app/[locale]/(user)/collaboratives/[collaborativeSlug]/CollaborativeDetailsClient.tsx @@ -1,6 +1,6 @@ 'use client'; -import { useEffect } from 'react'; +import { useEffect, useState } from 'react'; import Image from 'next/image'; import Link from 'next/link'; import { useParams } from 'next/navigation'; @@ -15,6 +15,7 @@ import { useQuery } from '@tanstack/react-query'; import { Card, Text } from 'opub-ui'; import { GraphQLPublic } from '@/lib/api'; +import { isCollaborativeSubdomainHost as isCollaborativeSubdomainHostname } from '@/lib/collaborativesRouting'; import { formatDate, generateJsonLd } from '@/lib/utils'; import BreadCrumbs from '@/components/BreadCrumbs'; import { Icons } from '@/components/icons'; @@ -217,9 +218,41 @@ const CollaborativeDetails = graphql(` } `); +const getPlatformEntityUrl = ( + entityPath: 'usecases' | 'datasets', + entityId: string | number, + locale?: string +) => { + //Removes trailing slash + const platformBaseUrl = (process.env.NEXT_PUBLIC_PLATFORM_URL || '').replace( + /\/$/, + '' + ); + const localeSegment = locale ? `/${locale}` : ''; + + if (!platformBaseUrl) { + return `/${entityPath}/${entityId}`; + } + + return `${platformBaseUrl}${localeSegment}/${entityPath}/${entityId}`; +}; + const CollaborativeDetailClient = () => { const params = useParams(); const { trackCollaborative } = useAnalytics(); + const locale = + typeof (params as any)?.locale === 'string' + ? (params as any).locale + : undefined; + const [isCollaborativeSubdomainHost, setIsCollaborativeSubdomainHost] = + useState(false); + + useEffect(() => { + if (typeof window === 'undefined') return; + setIsCollaborativeSubdomainHost( + isCollaborativeSubdomainHostname(window.location.hostname) + ); + }, []); const { data: CollaborativeDetailsData, @@ -250,12 +283,6 @@ const CollaborativeDetailClient = () => { } ); - console.log('Collaborative details query state:', { - isLoading, - error, - data: CollaborativeDetailsData, - }); - // Track collaborative view when data is loaded useEffect(() => { if (CollaborativeDetailsData?.collaborativeBySlug) { @@ -328,20 +355,23 @@ const CollaborativeDetailClient = () => { ) : ( <> - + {!isCollaborativeSubdomainHost && ( + + )}
-
+
{
{/* Use Cases Section */} {useCases.length > 0 && ( -
+
Use Cases @@ -499,7 +532,11 @@ const CollaborativeDetailClient = () => { key={useCase.id} variation={'collapsed'} iconColor="success" - href={`/usecases/${useCase.id}`} + href={getPlatformEntityUrl( + 'usecases', + useCase.id, + locale + )} /> ); })} @@ -507,7 +544,10 @@ const CollaborativeDetailClient = () => {
)} {/* Datasets Section */} -
+
Datasets in this Collaborative @@ -550,7 +590,11 @@ const CollaborativeDetailClient = () => { stroke: 1.2, }, ]} - href={`/datasets/${dataset.id}`} + href={getPlatformEntityUrl( + 'datasets', + dataset.id, + locale + )} leftFooterChips={[ { icon: `/Sectors/${dataset.sectors[0]?.name}.svg`, diff --git a/app/[locale]/(user)/layout.tsx b/app/[locale]/(user)/layout.tsx index 29f8f885..fb4a462f 100644 --- a/app/[locale]/(user)/layout.tsx +++ b/app/[locale]/(user)/layout.tsx @@ -1,10 +1,12 @@ 'use client'; import { notFound, usePathname } from 'next/navigation'; -import React from 'react'; +import React, { useEffect, useState } from 'react'; import MainFooter from '../dashboard/components/main-footer'; import { MainNav } from '../dashboard/components/main-nav'; +import { CollaborativeSubdomainNav } from '../dashboard/components/CollaborativeSubdomainNav'; +import { isCollaborativeSubdomainHost } from '@/lib/collaborativesRouting'; interface UserLayoutProps { children?: React.ReactNode; @@ -14,16 +16,41 @@ export default function Layout({ children }: UserLayoutProps) { const user = true; // await getCurrentUser() const routerPath = usePathname(); const hideSearch = routerPath === '/' || routerPath === '/datasets'; + const [isCollaborativeSubdomain, setIsCollaborativeSubdomain] = useState< + boolean | null + >(null); + const shouldHideMainNav = isCollaborativeSubdomain === true; + + useEffect(() => { + if (typeof window === 'undefined') { + setIsCollaborativeSubdomain(false); + return; + } + + setIsCollaborativeSubdomain( + isCollaborativeSubdomainHost(window.location.hostname) + ); + }, [routerPath]); if (!user) { return notFound(); } + const shouldRenderMainNav = + isCollaborativeSubdomain !== null && !shouldHideMainNav; + const shouldRenderCollaborativeSubdomainNav = isCollaborativeSubdomain === true; + return (
-
- -
+ {isCollaborativeSubdomain !== null && ( +
+ {shouldRenderMainNav ? ( + + ) : shouldRenderCollaborativeSubdomainNav ? ( + + ) : null} +
+ )}
{children}