@estation/create-cms-site 3.0.1 → 3.0.2
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
CHANGED
|
@@ -1,64 +1,75 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
3
|
import { usePathname } from "next/navigation";
|
|
4
|
+
import { useEffect, useState } from "react";
|
|
5
|
+
|
|
6
|
+
interface Crumb {
|
|
7
|
+
label: string;
|
|
8
|
+
href: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const HOME_LABELS: Record<string, string> = {
|
|
12
|
+
en: "Home", ar: "الرئيسية", ku: "سەرەکی", fr: "Accueil",
|
|
13
|
+
de: "Startseite", es: "Inicio", tr: "Ana Sayfa", it: "Home",
|
|
14
|
+
pt: "Início", zh: "首页", ja: "ホーム", ko: "홈",
|
|
15
|
+
};
|
|
4
16
|
|
|
5
17
|
/**
|
|
6
|
-
*
|
|
7
|
-
*
|
|
18
|
+
* Auto breadcrumbs — reads URL, fetches localized titles from CMS.
|
|
19
|
+
* No props needed — place once in the layout, works everywhere.
|
|
8
20
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* /en/partners → Home > Partners
|
|
13
|
-
* /en/partners/york → Home > Partners > York
|
|
21
|
+
* /en → (hidden)
|
|
22
|
+
* /en/about-us → Home > About Us (from CMS page title)
|
|
23
|
+
* /ar/partners/york → الرئيسية / شركاؤنا / يورك
|
|
14
24
|
*/
|
|
15
25
|
export function Breadcrumbs() {
|
|
16
26
|
const pathname = usePathname();
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
.
|
|
42
|
-
|
|
43
|
-
|
|
27
|
+
const [crumbs, setCrumbs] = useState<Crumb[]>([]);
|
|
28
|
+
|
|
29
|
+
useEffect(() => {
|
|
30
|
+
if (!pathname) return;
|
|
31
|
+
|
|
32
|
+
const segments = pathname.split("/").filter(Boolean);
|
|
33
|
+
const locale = segments[0] || "en";
|
|
34
|
+
const pathSegments = segments.slice(1);
|
|
35
|
+
|
|
36
|
+
if (pathSegments.length === 0) {
|
|
37
|
+
setCrumbs([]);
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const homeCrumb: Crumb = {
|
|
42
|
+
label: HOME_LABELS[locale] || "Home",
|
|
43
|
+
href: `/${locale}`,
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
// Show slug-based labels immediately
|
|
47
|
+
const initial: Crumb[] = [homeCrumb];
|
|
48
|
+
let p = `/${locale}`;
|
|
49
|
+
for (const seg of pathSegments) {
|
|
50
|
+
p += `/${seg}`;
|
|
51
|
+
initial.push({ label: slugToTitle(seg), href: p });
|
|
52
|
+
}
|
|
53
|
+
setCrumbs(initial);
|
|
54
|
+
|
|
55
|
+
// Then fetch real titles from CMS
|
|
56
|
+
fetchTitles(locale, pathSegments).then((fetched) => {
|
|
57
|
+
if (fetched.length > 0) setCrumbs([homeCrumb, ...fetched]);
|
|
58
|
+
});
|
|
59
|
+
}, [pathname]);
|
|
60
|
+
|
|
61
|
+
if (crumbs.length === 0) return null;
|
|
44
62
|
|
|
45
63
|
return (
|
|
46
|
-
<nav aria-label="Breadcrumb" className="px-6 py-3">
|
|
47
|
-
<ol className="flex items-center gap-1.5 text-sm text-gray-500">
|
|
64
|
+
<nav aria-label="Breadcrumb" className="px-6 py-3 max-w-6xl mx-auto">
|
|
65
|
+
<ol className="flex items-center gap-1.5 text-sm text-gray-500 flex-wrap">
|
|
48
66
|
{crumbs.map((crumb, idx) => (
|
|
49
67
|
<li key={crumb.href} className="flex items-center gap-1.5">
|
|
50
|
-
{idx > 0 &&
|
|
51
|
-
<span className="text-gray-300" aria-hidden="true">/</span>
|
|
52
|
-
)}
|
|
68
|
+
{idx > 0 && <span className="text-gray-300">/</span>}
|
|
53
69
|
{idx === crumbs.length - 1 ? (
|
|
54
70
|
<span className="text-gray-900 font-medium">{crumb.label}</span>
|
|
55
71
|
) : (
|
|
56
|
-
<a
|
|
57
|
-
href={crumb.href}
|
|
58
|
-
className="hover:text-gray-700 hover:underline transition-colors"
|
|
59
|
-
>
|
|
60
|
-
{crumb.label}
|
|
61
|
-
</a>
|
|
72
|
+
<a href={crumb.href} className="hover:text-gray-700 hover:underline transition-colors">{crumb.label}</a>
|
|
62
73
|
)}
|
|
63
74
|
</li>
|
|
64
75
|
))}
|
|
@@ -66,3 +77,43 @@ export function Breadcrumbs() {
|
|
|
66
77
|
</nav>
|
|
67
78
|
);
|
|
68
79
|
}
|
|
80
|
+
|
|
81
|
+
async function fetchTitles(locale: string, segments: string[]): Promise<Crumb[]> {
|
|
82
|
+
const apiUrl = process.env.NEXT_PUBLIC_CMS_API_URL || "https://cms-gateway.estation.io/api/v1";
|
|
83
|
+
const token = process.env.NEXT_PUBLIC_CMS_API_TOKEN || "";
|
|
84
|
+
const v2 = apiUrl.replace(/\/api\/v[12]$/, "/api/v2");
|
|
85
|
+
const crumbs: Crumb[] = [];
|
|
86
|
+
let path = `/${locale}`;
|
|
87
|
+
|
|
88
|
+
for (let i = 0; i < segments.length; i++) {
|
|
89
|
+
path += `/${segments[i]}`;
|
|
90
|
+
let label = slugToTitle(segments[i]);
|
|
91
|
+
|
|
92
|
+
try {
|
|
93
|
+
if (i === 0) {
|
|
94
|
+
const r = await fetch(`${apiUrl}/public/content/pages/slug/${segments[i]}?locale=${locale}`, { headers: { "X-API-TOKEN": token }, cache: "force-cache" });
|
|
95
|
+
if (r.ok) {
|
|
96
|
+
const j = await r.json();
|
|
97
|
+
const pg = j.data?.page || j.page;
|
|
98
|
+
if (pg) label = pg.metadata?.titles?.[locale] || pg.title || label;
|
|
99
|
+
}
|
|
100
|
+
} else if (i === 1) {
|
|
101
|
+
const type = deriveType(segments[0]);
|
|
102
|
+
const r = await fetch(`${v2}/public/content/${type}/slug/${segments[i]}`, { headers: { "X-API-TOKEN": token }, cache: "force-cache" });
|
|
103
|
+
if (r.ok) {
|
|
104
|
+
const j = await r.json();
|
|
105
|
+
const b = j.data || j;
|
|
106
|
+
const fv = b?.content?.title?.fieldValue;
|
|
107
|
+
if (fv) label = typeof fv === "string" ? fv : (fv[locale] || fv.en || Object.values(fv)[0] as string || label);
|
|
108
|
+
else if (b?.name) label = b.name;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
} catch { /* fallback */ }
|
|
112
|
+
|
|
113
|
+
crumbs.push({ label, href: path });
|
|
114
|
+
}
|
|
115
|
+
return crumbs;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function slugToTitle(s: string) { return s.split("-").map(w => w[0].toUpperCase() + w.slice(1)).join(" "); }
|
|
119
|
+
function deriveType(s: string) { return s.endsWith("ies") ? s.slice(0,-3)+"y" : s.endsWith("s") ? s.slice(0,-1) : s; }
|