Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion .env.local.example
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/deploy-Dataspace.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -336,7 +337,7 @@ const CollaborativesListingClient = () => {
stroke: 1.2,
},
]}
href={`/collaboratives/${collaborative.slug}`}
href={getCollaborativeDetailUrl(collaborative.slug)}
leftFooterChips={[
{
icon: collaborative.sectors?.[0]?.name
Expand Down
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -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';
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -328,20 +355,23 @@ const CollaborativeDetailClient = () => {
</div>
) : (
<>
<BreadCrumbs
data={[
{ href: '/', label: 'Home' },
{ href: '/collaboratives', label: 'Collaboratives' },
{
href: '#',
label:
CollaborativeDetailsData?.collaborativeBySlug?.title || '',
},
]}
/>
{!isCollaborativeSubdomainHost && (
<BreadCrumbs
data={[
{ href: '/', label: 'Home' },
{ href: '/collaboratives', label: 'Collaboratives' },
{
href: '#',
label:
CollaborativeDetailsData?.collaborativeBySlug?.title ||
'',
},
]}
/>
)}
<div className=" bg-primaryBlue">
<div className="container flex flex-row">
<div className="w-full border-solid border-baseGraySlateSolid9 py-8 pr-8 lg:w-3/4 lg:border-r-2 lg:py-10 lg:pr-8">
<div className="w-full border-solid border-baseGraySlateSolid9 py-8 pr-8 lg:w-3/4 lg:py-10 lg:pr-8">
<PrimaryDetails
data={CollaborativeDetailsData}
isLoading={isLoading}
Expand Down Expand Up @@ -415,7 +445,10 @@ const CollaborativeDetailClient = () => {
<div className="container py-8 lg:py-14" color="onBgDefault">
{/* Use Cases Section */}
{useCases.length > 0 && (
<div className="container py-8 lg:py-14">
<div
id="collaborative-use-cases"
className="container scroll-mt-28 py-8 lg:py-14"
>
<div className=" flex flex-col gap-1 ">
<Text variant="headingXl">Use Cases</Text>
<Text variant="bodyLg" fontWeight="regular">
Expand Down Expand Up @@ -499,15 +532,22 @@ const CollaborativeDetailClient = () => {
key={useCase.id}
variation={'collapsed'}
iconColor="success"
href={`/usecases/${useCase.id}`}
href={getPlatformEntityUrl(
'usecases',
useCase.id,
locale
)}
/>
);
})}
</div>
</div>
)}
{/* Datasets Section */}
<div className="container py-8 lg:py-14">
<div
id="collaborative-datasets"
className="container scroll-mt-28 py-8 lg:py-14"
>
<div className=" flex flex-col gap-1 ">
<Text variant="headingXl">
Datasets in this Collaborative
Expand Down Expand Up @@ -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`,
Expand Down
35 changes: 31 additions & 4 deletions app/[locale]/(user)/layout.tsx
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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 (
<div className="flex min-h-screen flex-col">
<header className="z-1 sticky top-0 bg-primaryBlue">
<MainNav hideSearch={hideSearch} />
</header>
{isCollaborativeSubdomain !== null && (
<header className="z-1 sticky top-0 bg-primaryBlue">
{shouldRenderMainNav ? (
<MainNav hideSearch={hideSearch} />
) : shouldRenderCollaborativeSubdomainNav ? (
<CollaborativeSubdomainNav />
) : null}
</header>
)}
<main className="grow">{children}</main>
<footer>
<MainFooter />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
} from 'opub-ui';

import { cn, formatDate } from '@/lib/utils';
import { getCollaborativeDetailUrl } from '@/lib/collaborativesRouting';
import BreadCrumbs from '@/components/BreadCrumbs';
import { Icons } from '@/components/icons';
import { Loading } from '@/components/loading';
Expand Down Expand Up @@ -489,7 +490,7 @@ const UnifiedListingComponent: React.FC<UnifiedListingProps> = ({
case 'aimodel':
return `/aimodels/${item.id}`;
case 'collaborative':
return `/collaboratives/${item.slug}`;
return getCollaborativeDetailUrl(item.slug);
case 'publisher':
// For publishers, redirect based on publisher_type
if (item.publisher_type === 'organization') {
Expand Down
76 changes: 76 additions & 0 deletions app/[locale]/dashboard/components/CollaborativeSubdomainNav.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
'use client';

import Image from 'next/image';
import Link from 'next/link';
import { Text } from 'opub-ui';
import { getPlatformRootUrl } from '@/lib/collaborativesRouting';

type Props = {
useCasesTargetId?: string;
datasetsTargetId?: string;
};

const scrollToId = (id: string) => {
const el = document.getElementById(id);
if (!el) return;
el.scrollIntoView({ behavior: 'smooth', block: 'start' });
};

export function CollaborativeSubdomainNav({
useCasesTargetId = 'collaborative-use-cases',
datasetsTargetId = 'collaborative-datasets',
}: Props) {
return (
<nav className="z-50 sticky top-1 min-h-[80px] border-b-1 border-solid border-baseGraySlateSolid9 px-4 py-6 sm:py-8 md:py-5 lg:px-1 lg:py-3">
<div className=" flex items-center justify-between gap-4">
<div className="flex items-center gap-1">
<Link href={getPlatformRootUrl()}>
<div className="flex items-center gap-2">
<div className="group relative h-[35px] w-[130px] overflow-hidden md:h-[40px] md:w-[150px] lg:h-[68px] lg:w-[183px]">
<div className="absolute inset-0">
<Image
src="/dataspacelogosep2025.png"
alt="Logo"
fill
className="object-contain"
/>
</div>
</div>
</div>
</Link>
</div>

<div className="mr-10 flex items-center gap-6">
<button
type="button"
onClick={() => scrollToId(useCasesTargetId)}
className="cursor-pointer border-none bg-transparent p-0 outline-none"
>
<Text
variant="headingMd"
as="span"
className="uppercase text-surfaceDefault hover:text-[#84DCCF]"
fontWeight="semibold"
>
Use Cases
</Text>
</button>
<button
type="button"
onClick={() => scrollToId(datasetsTargetId)}
className="cursor-pointer border-none bg-transparent p-0 outline-none"
>
<Text
variant="headingMd"
as="span"
className="uppercase text-surfaceDefault hover:text-[#84DCCF]"
fontWeight="semibold"
>
Datasets
</Text>
</button>
</div>
</div>
</nav>
);
}
Loading
Loading