Skip to content

Commit

Permalink
Fetch Paths data and display settings bar
Browse files Browse the repository at this point in the history
  • Loading branch information
terotik committed Feb 25, 2025
1 parent 75ba941 commit 1ba411f
Show file tree
Hide file tree
Showing 30 changed files with 6,014 additions and 35 deletions.
22 changes: 22 additions & 0 deletions apollo-paths.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// eslint-disable-next-line @typescript-eslint/no-var-requires
require('dotenv').config();

const { pathsGqlUrl } = require('./common/environment');

const JS = '*.{js,jsx,ts,tsx,mjs}';

module.exports = {
client: {
includes: [
'common/paths/**/*.{js,ts,tsx}',
'components/paths/**/*.{js,ts,tsx}',
'context/paths/**/*.{js,ts,tsx}',
'queries/paths/**/*.{js,ts,tsx}',
],
excludes: ['components/paths/contentblocks/**/*.{js,ts,tsx}'],
service: {
name: 'kausal-paths',
url: `${pathsGqlUrl}`,
},
},
};
6 changes: 6 additions & 0 deletions apollo.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,12 @@ module.exports = {
'pages/**/*.{js,ts,tsx}',
'queries/**/*.{js,ts,tsx}',
],
excludes: [
'common/paths/**/*.{js,ts,tsx}',
'components/paths/**/*.{js,ts,tsx}',
'context/paths/**/*.{js,ts,tsx}',
'queries/paths/**/*.{js,ts,tsx}',
],
service: {
name: 'kausal-watch-backend',
url: `${apiUrl}/graphql/`,
Expand Down
7 changes: 5 additions & 2 deletions app/[domain]/[lang]/[plan]/(with-layout-elements)/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import { ReactNode } from 'react';

import Header from '@/components/Header';
import Footer from '@/components/Footer';
import { StyledMain } from './StyledMain';
import Header from '@/components/Header';
import SettingsPanelFull from '@/components/paths/toolbar/SettingsPanelFull';

import { JsonLd } from './JsonLd';
import { StyledMain } from './StyledMain';

type Props = {
params: { domain: string };
Expand All @@ -21,6 +23,7 @@ export default function Layout({ children, params }: Props) {
<Header />
<StyledMain id="main">{children}</StyledMain>
<Footer />
<SettingsPanelFull />
</>
);
}
63 changes: 41 additions & 22 deletions app/[domain]/[lang]/[plan]/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,24 @@
import { ReactNode } from 'react';
import { notFound } from 'next/navigation';

import { Metadata } from 'next';
import { cookies, headers } from 'next/headers';
import { captureException } from '@sentry/nextjs';
import { notFound } from 'next/navigation';

import ThemeProvider from '@/components/providers/ThemeProvider';
import PlanProvider from '@/components/providers/PlanProvider';
import { getPlan } from '@/queries/get-plan';
import { GlobalStyles } from '@/styles/GlobalStyles';
import { getThemeStaticURL, loadTheme } from '@/common/theme';
import { SharedIcons } from '@/components/common/Icon';
import { MatomoAnalytics } from '@/components/MatomoAnalytics';
import { getMetaTitles } from '@/utils/metadata';
import { tryRequest } from '@/utils/api.utils';
import { UpdateApolloContext } from './UpdateApolloContext';
import PathsProvider from '@/components/providers/PathsProvider';
import PlanProvider from '@/components/providers/PlanProvider';
import ThemeProvider from '@/components/providers/ThemeProvider';
import { SELECTED_WORKFLOW_COOKIE_KEY } from '@/constants/workflow';
import { WorkflowProvider } from '@/context/workflow-selector';

import { getPlan } from '@/queries/get-plan';
import { getPathsInstance } from '@/queries/paths/get-paths-instance';
import { GlobalStyles } from '@/styles/GlobalStyles';
import { tryRequest } from '@/utils/api.utils';
import { getMetaTitles } from '@/utils/metadata';
import { captureException } from '@sentry/nextjs';
import { GetInstanceContextQuery } from '@/common/__generated__/paths/graphql';
type Props = {
params: { plan: string; domain: string; lang: string };
children: ReactNode;
Expand Down Expand Up @@ -90,23 +92,39 @@ export async function generateMetadata({ params }: Props): Promise<Metadata> {
};
}

async function getPathsData(pathsInstance: string) {
if (pathsInstance) {
const { data: pathsData } = await tryRequest<GetInstanceContextQuery>(
getPathsInstance(pathsInstance)
);
if (pathsData?.instance) {
// console.log('pathsData', pathsData);
return pathsData;
} else return undefined;
}
return undefined;
}

export default async function PlanLayout({ params, children }: Props) {
const { plan, domain } = params;
const headersList = headers();
const cookieStore = cookies();
const protocol = headersList.get('x-forwarded-proto');
const { data } = await tryRequest(
const { data: planData } = await tryRequest(
getPlan(domain, plan, `${protocol}://${domain}`)
);

if (!data?.plan) {
if (!planData?.plan) {
notFound();
}

const theme = await loadTheme(data.plan.themeIdentifier || params.plan);
const matomoAnalyticsUrl = data.plan.domain?.matomoAnalyticsUrl ?? undefined;
const theme = await loadTheme(planData.plan.themeIdentifier || params.plan);
const matomoAnalyticsUrl =
planData.plan.domain?.matomoAnalyticsUrl ?? undefined;
const selectedWorkflow = cookieStore.get(SELECTED_WORKFLOW_COOKIE_KEY);

const pathsData = await getPathsData(planData.plan?.kausalPathsInstanceUuid);

return (
<>
{theme.name && (
Expand All @@ -124,14 +142,15 @@ export default async function PlanLayout({ params, children }: Props) {
<ThemeProvider theme={theme}>
<GlobalStyles />
<SharedIcons />
<PlanProvider plan={data.plan}>
<WorkflowProvider
initialWorkflow={selectedWorkflow?.value as string | undefined}
workflowStates={data.workflowStates}
>
<UpdateApolloContext domain={domain} />
{children}
</WorkflowProvider>
<PlanProvider plan={planData.plan}>
<PathsProvider instance={pathsData}>
<WorkflowProvider
initialWorkflow={selectedWorkflow?.value as string | undefined}
workflowStates={planData.workflowStates}
>
{children}
</WorkflowProvider>
</PathsProvider>
</PlanProvider>
</ThemeProvider>
</>
Expand Down
91 changes: 91 additions & 0 deletions app/api/graphql-paths/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import { headers } from 'next/headers';
import { NextRequest, NextResponse } from 'next/server';

import { captureException } from '@sentry/nextjs';

import { forwardSetCookie, getClientCookieAsHeader } from '@/common/cookies';
import { pathsGqlUrl } from '@/common/environment';

export const dynamic = 'force-dynamic';

const PASS_HEADERS = [
'x-paths-instance-identifier',
'x-paths-instance-hostname',
'x-wildcard-domains',
'user-agent',
'authorization',
'accept-language',
'dnt',
'referer',
];

const PATHS_COOKIE_PREFIX = 'paths_api_';

export async function POST(request: NextRequest) {
const headersList = headers();
const requestData = await request.json();
const backendCookieHeader = getClientCookieAsHeader(request, {
prefix: PATHS_COOKIE_PREFIX,
});

// Determine headers to send to the backend
const backendHeaders: Record<string, string> = {};
PASS_HEADERS.forEach((header) => {
const value = headersList.get(header);
if (value) backendHeaders[header] = value;
});
backendHeaders['Content-Type'] = 'application/json';
if (backendCookieHeader) {
backendHeaders['Cookie'] = backendCookieHeader;
}

// Do the fetch from the backend
const backendResponse = await fetch(pathsGqlUrl, {
method: 'POST',
headers: backendHeaders,
body: JSON.stringify(requestData),
next: { revalidate: 0 },
});

// Set response headers
const responseHeaders: Record<string, string> = {
'Content-Type':
backendResponse.headers.get('Content-Type') ?? 'application/json',
'Content-Language': backendResponse.headers.get('Content-Language') ?? '',
'Cache-Control': 'no-store',
};

if (!backendResponse.ok) {
console.error('Backend responded with ', backendResponse.status);
let data: object | undefined, errorMessage: string | undefined;
try {
if (backendResponse.headers.get('content-type') === 'application/json') {
data = await backendResponse.json();
}
} catch (error) {
captureException(error);
}
if (!data) {
errorMessage = await backendResponse.text();
data = { errors: [{ message: errorMessage }] };
}
if (process.env.NODE_ENV !== 'production') {
console.log(data);
}
return NextResponse.json(data, {
status: backendResponse.status,
headers: responseHeaders,
});
}

try {
const data = await backendResponse.json();
forwardSetCookie(request, backendResponse, { prefix: PATHS_COOKIE_PREFIX });
return NextResponse.json(data, { status: 200, headers: responseHeaders });
} catch (error) {
return NextResponse.json(
{ errors: [{ message: `Response is invalid JSON: ${error?.message}` }] },
{ status: 500, headers: responseHeaders }
);
}
}
37 changes: 34 additions & 3 deletions codegen.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,54 @@
import { CodegenConfig } from '@graphql-codegen/cli';
import type { TypeScriptPluginConfig } from '@graphql-codegen/typescript';
import { TypeScriptDocumentsPluginConfig } from '@graphql-codegen/typescript-operations';

import pathsApolloConfig from './apollo-paths.config';
import apolloConfig from './apollo.config';

const tsoConfig: TypeScriptDocumentsPluginConfig = {
const tsoConfig: TypeScriptDocumentsPluginConfig & TypeScriptPluginConfig = {
arrayInputCoercion: false,
mergeFragmentTypes: true,
};

const watchConfigDocs = [
...apolloConfig.client.includes,
...apolloConfig.client.excludes.map((exclude) => `!${exclude}`),
];

const pathsConfigDocs = [
...pathsApolloConfig.client.includes,
...pathsApolloConfig.client.excludes.map((exclude) => `!${exclude}`),
];

const config: CodegenConfig = {
schema: apolloConfig.client.service.url,
documents: apolloConfig.client.includes,
overwrite: true,

generates: {
'common/__generated__/possible_types.json': {
schema: apolloConfig.client.service.url,
documents: watchConfigDocs,
plugins: ['fragment-matcher'],
config: {
useExplicitTyping: true,
},
},
'common/__generated__/graphql.ts': {
schema: apolloConfig.client.service.url,
documents: watchConfigDocs,
plugins: ['typescript', 'typescript-operations'],
config: tsoConfig,
},
'common/__generated__/paths/possible_types.json': {
schema: pathsApolloConfig.client.service.url,
documents: pathsConfigDocs,
plugins: ['fragment-matcher'],
config: {
useExplicitTyping: true,
},
},
'common/__generated__/paths/graphql.ts': {
schema: pathsApolloConfig.client.service.url,
documents: pathsConfigDocs,
plugins: ['typescript', 'typescript-operations'],
config: tsoConfig,
},
Expand Down
Loading

0 comments on commit 1ba411f

Please sign in to comment.