@c-rex/templates 0.3.0-build.42 → 0.3.0-build.50
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +9 -4
- package/src/home/layout.tsx +54 -46
- package/src/layout.tsx +104 -67
- package/src/utils.ts +8 -8
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@c-rex/templates",
|
|
3
|
-
"version": "0.3.0-build.
|
|
3
|
+
"version": "0.3.0-build.50",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"files": [
|
|
6
6
|
"src"
|
|
@@ -21,7 +21,12 @@
|
|
|
21
21
|
},
|
|
22
22
|
"scripts": {
|
|
23
23
|
"lint": "eslint .",
|
|
24
|
-
"lint:fix": "eslint . --fix"
|
|
24
|
+
"lint:fix": "eslint . --fix",
|
|
25
|
+
"check-types": "echo \u0027No types to check\u0027",
|
|
26
|
+
"test": "echo \u0027No tests\u0027",
|
|
27
|
+
"build": "echo \u0027No build script\u0027",
|
|
28
|
+
"format:check": "prettier --check .",
|
|
29
|
+
"validate": "npm run lint \u0026\u0026 npm run check-types \u0026\u0026 npm run test \u0026\u0026 npm run build"
|
|
25
30
|
},
|
|
26
31
|
"devDependencies": {
|
|
27
32
|
"@c-rex/eslint-config": "*",
|
|
@@ -31,7 +36,7 @@
|
|
|
31
36
|
"@types/react": "19.0.10",
|
|
32
37
|
"@types/react-dom": "19.0.4",
|
|
33
38
|
"eslint": "^9.23.0",
|
|
34
|
-
"typescript": "
|
|
39
|
+
"typescript": "~5.8.3"
|
|
35
40
|
},
|
|
36
41
|
"dependencies": {
|
|
37
42
|
"@c-rex/components": "*",
|
|
@@ -45,7 +50,7 @@
|
|
|
45
50
|
"@c-rex/utils": "*",
|
|
46
51
|
"cheerio": "^1.1.0",
|
|
47
52
|
"i18next": "^25.1.3",
|
|
48
|
-
"next": "^
|
|
53
|
+
"next": "^15",
|
|
49
54
|
"next-intl": "^4.1.0",
|
|
50
55
|
"nuqs": "^2.4.3",
|
|
51
56
|
"react": "^18.3.1",
|
package/src/home/layout.tsx
CHANGED
|
@@ -6,58 +6,66 @@ import { informationUnitsResponse } from "@c-rex/interfaces";
|
|
|
6
6
|
import { CrexSDK } from "@c-rex/core/sdk";
|
|
7
7
|
|
|
8
8
|
interface HomeProps {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
9
|
+
searchParams: {
|
|
10
|
+
search?: string;
|
|
11
|
+
page: string;
|
|
12
|
+
language: string;
|
|
13
|
+
wildcard: string;
|
|
14
|
+
operator: string;
|
|
15
|
+
like: string;
|
|
16
|
+
packages?: string;
|
|
17
|
+
filter?: string;
|
|
18
|
+
};
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
export const HomeLayout = async ({ searchParams }: HomeProps) => {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
22
|
+
const { search, page, language, wildcard, operator, like, packages, filter } =
|
|
23
|
+
searchParams;
|
|
24
|
+
const sdk = new CrexSDK();
|
|
25
|
+
const config = sdk.getServerConfig();
|
|
25
26
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
27
|
+
let data = {
|
|
28
|
+
items: [],
|
|
29
|
+
pageInfo: {
|
|
30
|
+
pageCount: 0,
|
|
31
|
+
pageNumber: 0,
|
|
32
|
+
},
|
|
33
|
+
} as unknown as informationUnitsResponse;
|
|
33
34
|
|
|
34
|
-
|
|
35
|
+
if (search !== undefined) {
|
|
36
|
+
const filters: string[] = filter?.split(",") || [];
|
|
37
|
+
const restrict: string[] = [];
|
|
35
38
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
if (packages && packages.length > 0) {
|
|
40
|
-
restrict.push(`packages.shortId=${packages}`)
|
|
41
|
-
}
|
|
42
|
-
const service = new InformationUnitsService();
|
|
43
|
-
|
|
44
|
-
data = await service.getList({
|
|
45
|
-
queries: search,
|
|
46
|
-
page: Number(page),
|
|
47
|
-
fields: ["renditions", "class", "languages", "labels", "versionOf", "revision"],
|
|
48
|
-
languages: language.split(","),
|
|
49
|
-
wildcard: wildcard as WildCardType,
|
|
50
|
-
restrict: restrict,
|
|
51
|
-
operator: operator as OperatorType,
|
|
52
|
-
like: Boolean(like === "true"),
|
|
53
|
-
filters: filters,
|
|
54
|
-
tags: config.search.tags,
|
|
55
|
-
});
|
|
39
|
+
if (packages && packages.length > 0) {
|
|
40
|
+
restrict.push(`packages.shortId=${packages}`);
|
|
56
41
|
}
|
|
42
|
+
const service = new InformationUnitsService();
|
|
43
|
+
|
|
44
|
+
data = await service.getList({
|
|
45
|
+
queries: search,
|
|
46
|
+
page: Number(page),
|
|
47
|
+
fields: [
|
|
48
|
+
"renditions",
|
|
49
|
+
"class",
|
|
50
|
+
"languages",
|
|
51
|
+
"titles",
|
|
52
|
+
"labels",
|
|
53
|
+
"versionOf",
|
|
54
|
+
"revision",
|
|
55
|
+
],
|
|
56
|
+
languages: language.split(","),
|
|
57
|
+
wildcard: wildcard as WildCardType,
|
|
58
|
+
restrict: restrict,
|
|
59
|
+
operator: operator as OperatorType,
|
|
60
|
+
like: Boolean(like === "true"),
|
|
61
|
+
filters: filters,
|
|
62
|
+
tags: config.search.tags,
|
|
63
|
+
});
|
|
64
|
+
}
|
|
57
65
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
66
|
+
return (
|
|
67
|
+
<PageWrapper>
|
|
68
|
+
<HomePage data={data} />
|
|
69
|
+
</PageWrapper>
|
|
70
|
+
);
|
|
63
71
|
};
|
package/src/layout.tsx
CHANGED
|
@@ -1,91 +1,128 @@
|
|
|
1
1
|
import React from "react";
|
|
2
|
-
import { NextIntlClientProvider } from
|
|
2
|
+
import { NextIntlClientProvider } from "next-intl";
|
|
3
3
|
import { getLocale } from "next-intl/server";
|
|
4
|
-
import { Toaster } from "@c-rex/ui/sonner"
|
|
4
|
+
import { Toaster } from "@c-rex/ui/sonner";
|
|
5
5
|
import { AppConfigProvider } from "@c-rex/contexts/config-provider";
|
|
6
|
-
import { NuqsAdapter } from
|
|
6
|
+
import { NuqsAdapter } from "nuqs/adapters/next/app";
|
|
7
7
|
import { cookies } from "next/headers";
|
|
8
8
|
import { CrexSDK } from "@c-rex/core/sdk";
|
|
9
9
|
import { getIssuerMetadata } from "@c-rex/core/OIDC";
|
|
10
10
|
import { resolveUILanguage, resolveContentLanguage } from "./utils";
|
|
11
11
|
import { informationUnitsLanguagesServer } from "@c-rex/services/server-requests";
|
|
12
|
-
import {
|
|
12
|
+
import {
|
|
13
|
+
UI_LANG_KEY,
|
|
14
|
+
SEARCH_SETTINGS_COOKIE_NAME,
|
|
15
|
+
UI_PREFERENCES_COOKIE_NAME,
|
|
16
|
+
} from "@c-rex/constants";
|
|
13
17
|
import { transformLanguageData } from "@c-rex/utils";
|
|
14
18
|
import { getSearchSettingsFromCookie } from "@c-rex/components/search-settings-store";
|
|
15
19
|
import { FavoritesProvider } from "@c-rex/components/favorites-context";
|
|
20
|
+
import { getUiPreferencesFromCookie } from "@c-rex/components/ui-preferences-store";
|
|
21
|
+
import { UiPreferencesRuntime } from "@c-rex/components/ui-preferences-runtime";
|
|
16
22
|
|
|
17
23
|
interface DefaultRootLayoutProps {
|
|
18
|
-
|
|
24
|
+
children: React.ReactNode;
|
|
19
25
|
}
|
|
20
26
|
|
|
21
|
-
const getAvailableLanguagesSafe = async (): Promise<
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
27
|
+
const getAvailableLanguagesSafe = async (): Promise<
|
|
28
|
+
{ value: string; score: number }[]
|
|
29
|
+
> => {
|
|
30
|
+
try {
|
|
31
|
+
const data = await informationUnitsLanguagesServer();
|
|
32
|
+
return (data as { value: string; score: number }[]) || [];
|
|
33
|
+
} catch (error) {
|
|
34
|
+
// Some installations expose the Languages endpoint only intermittently
|
|
35
|
+
// or behind infrastructure that can transiently return 503. The app
|
|
36
|
+
// should still boot with the configured default language instead of
|
|
37
|
+
// failing the entire root layout.
|
|
38
|
+
console.warn(
|
|
39
|
+
"[DefaultRootLayout] Failed to load available content languages. Falling back to defaults.",
|
|
40
|
+
error,
|
|
41
|
+
);
|
|
42
|
+
return [];
|
|
43
|
+
}
|
|
33
44
|
};
|
|
34
45
|
|
|
35
|
-
export const DefaultRootLayout = async ({
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
46
|
+
export const DefaultRootLayout = async ({
|
|
47
|
+
children,
|
|
48
|
+
}: DefaultRootLayoutProps) => {
|
|
49
|
+
const [locale, metadata, languageData] = await Promise.all([
|
|
50
|
+
getLocale(),
|
|
51
|
+
getIssuerMetadata(),
|
|
52
|
+
getAvailableLanguagesSafe(),
|
|
53
|
+
]);
|
|
41
54
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
55
|
+
const sdk = new CrexSDK();
|
|
56
|
+
const cookieStore = await cookies();
|
|
57
|
+
const uiLangCookie = cookieStore.get(UI_LANG_KEY)?.value ?? null;
|
|
58
|
+
const clientConfigs = await sdk.getClientConfig();
|
|
59
|
+
const serverConfigs = sdk.getServerConfig();
|
|
60
|
+
const searchSettings = getSearchSettingsFromCookie(
|
|
61
|
+
cookieStore.get(SEARCH_SETTINGS_COOKIE_NAME)?.value,
|
|
62
|
+
);
|
|
63
|
+
const uiPreferences = getUiPreferencesFromCookie(
|
|
64
|
+
cookieStore.get(UI_PREFERENCES_COOKIE_NAME)?.value,
|
|
65
|
+
);
|
|
48
66
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
67
|
+
sdk.updateConfigProp("OIDC", {
|
|
68
|
+
...serverConfigs.OIDC,
|
|
69
|
+
issuerMetadata: metadata,
|
|
70
|
+
});
|
|
53
71
|
|
|
54
|
-
|
|
72
|
+
const availableLanguages = transformLanguageData(languageData);
|
|
55
73
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
74
|
+
const uiLang = await resolveUILanguage({
|
|
75
|
+
uiLangCookie,
|
|
76
|
+
defaultLang: clientConfigs.languageSwitcher.default,
|
|
77
|
+
});
|
|
60
78
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
79
|
+
const contentLang = await resolveContentLanguage({
|
|
80
|
+
contentLangCookie: searchSettings.language,
|
|
81
|
+
availableLanguages,
|
|
82
|
+
defaultLang:
|
|
83
|
+
clientConfigs.defaultContentLanguage ??
|
|
84
|
+
clientConfigs.languageSwitcher.default,
|
|
85
|
+
});
|
|
66
86
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
87
|
+
return (
|
|
88
|
+
<html
|
|
89
|
+
lang={locale}
|
|
90
|
+
className={uiPreferences.theme === "dark" ? "dark" : undefined}
|
|
91
|
+
data-theme={uiPreferences.theme}
|
|
92
|
+
data-font-scale={uiPreferences.fontScale}
|
|
93
|
+
data-readonly-mode={clientConfigs.readOnlyMode ? "true" : "false"}
|
|
94
|
+
style={{
|
|
95
|
+
fontSize:
|
|
96
|
+
uiPreferences.fontScale === "small"
|
|
97
|
+
? "93.75%"
|
|
98
|
+
: uiPreferences.fontScale === "large"
|
|
99
|
+
? "112.5%"
|
|
100
|
+
: "100%",
|
|
101
|
+
}}
|
|
102
|
+
>
|
|
103
|
+
<head>
|
|
104
|
+
<link rel="icon" type="image/x-icon" href="/img/favicon.png" />
|
|
105
|
+
</head>
|
|
106
|
+
<body className="min-h-screen bg-background font-sans antialiased">
|
|
107
|
+
<NextIntlClientProvider>
|
|
108
|
+
<AppConfigProvider
|
|
109
|
+
uiLang={uiLang}
|
|
110
|
+
contentLang={contentLang}
|
|
111
|
+
availableLanguages={availableLanguages}
|
|
112
|
+
initialConfig={clientConfigs}
|
|
113
|
+
loginEnabled={Boolean(serverConfigs.OIDC.client.enabled)}
|
|
114
|
+
>
|
|
115
|
+
<FavoritesProvider>
|
|
116
|
+
<UiPreferencesRuntime
|
|
117
|
+
initialTheme={uiPreferences.theme}
|
|
118
|
+
initialFontScale={uiPreferences.fontScale}
|
|
119
|
+
/>
|
|
120
|
+
<Toaster />
|
|
121
|
+
<NuqsAdapter>{children}</NuqsAdapter>
|
|
122
|
+
</FavoritesProvider>
|
|
123
|
+
</AppConfigProvider>
|
|
124
|
+
</NextIntlClientProvider>
|
|
125
|
+
</body>
|
|
126
|
+
</html>
|
|
127
|
+
);
|
|
91
128
|
};
|
package/src/utils.ts
CHANGED
|
@@ -2,8 +2,8 @@ import { UI_LANG_OPTIONS } from "@c-rex/constants";
|
|
|
2
2
|
import { headers } from "next/headers";
|
|
3
3
|
import { normalizeLanguageCode } from "@c-rex/utils";
|
|
4
4
|
|
|
5
|
-
const getBrowserLangFromHeader = (): string => {
|
|
6
|
-
const h = headers();
|
|
5
|
+
const getBrowserLangFromHeader = async (): Promise<string> => {
|
|
6
|
+
const h = await headers();
|
|
7
7
|
const accept = h.get("accept-language"); // ex: "pt-BR,pt;q=0.9,en-US;q=0.8,en;q=0.7"
|
|
8
8
|
|
|
9
9
|
if (!accept) return "";
|
|
@@ -12,14 +12,14 @@ const getBrowserLangFromHeader = (): string => {
|
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
|
|
15
|
-
export const resolveUILanguage = ({
|
|
15
|
+
export const resolveUILanguage = async ({
|
|
16
16
|
uiLangCookie,
|
|
17
17
|
defaultLang,
|
|
18
18
|
}: {
|
|
19
19
|
uiLangCookie?: string | null;
|
|
20
20
|
defaultLang: string;
|
|
21
|
-
}): string => {
|
|
22
|
-
const browserLang = getBrowserLangFromHeader();
|
|
21
|
+
}): Promise<string> => {
|
|
22
|
+
const browserLang = await getBrowserLangFromHeader();
|
|
23
23
|
|
|
24
24
|
if (uiLangCookie) return uiLangCookie;
|
|
25
25
|
if (browserLang && UI_LANG_OPTIONS.includes(browserLang)) return browserLang;
|
|
@@ -27,7 +27,7 @@ export const resolveUILanguage = ({
|
|
|
27
27
|
return defaultLang;
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
-
export const resolveContentLanguage = ({
|
|
30
|
+
export const resolveContentLanguage = async ({
|
|
31
31
|
contentLangCookie,
|
|
32
32
|
availableLanguages,
|
|
33
33
|
defaultLang,
|
|
@@ -35,8 +35,8 @@ export const resolveContentLanguage = ({
|
|
|
35
35
|
contentLangCookie?: string | null;
|
|
36
36
|
availableLanguages: { value: string }[];
|
|
37
37
|
defaultLang: string;
|
|
38
|
-
}): string => {
|
|
39
|
-
const browserLang = getBrowserLangFromHeader();
|
|
38
|
+
}): Promise<string> => {
|
|
39
|
+
const browserLang = await getBrowserLangFromHeader();
|
|
40
40
|
const normalizedAvailableLanguages = availableLanguages.map((item) => normalizeLanguageCode(item.value));
|
|
41
41
|
const normalizedDefaultLanguage = normalizeLanguageCode(defaultLang);
|
|
42
42
|
const normalizedCookieLanguage = contentLangCookie ? normalizeLanguageCode(contentLangCookie) : "";
|