@estation/create-cms-site 2.5.1 → 2.6.0

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.
@@ -4,8 +4,8 @@ import { getAuthHeaders } from "../utils/api-client.js";
4
4
  const CMS_BASE_URL = "https://cms-gateway.estation.io/api/v1";
5
5
  export async function uploadFile(filePath) {
6
6
  const headers = getAuthHeaders();
7
- if (!headers["Authorization"] && !headers["X-API-TOKEN"]) {
8
- throw new Error("Not authenticated. Use login or set_api_token first.");
7
+ if (!headers["Authorization"] && !headers["X-API-TOKEN"] && !headers["X-MCP-TOKEN"]) {
8
+ throw new Error("Not authenticated. Use login, set_api_token, or set_mcp_token first.");
9
9
  }
10
10
  const fileBuffer = await readFile(filePath);
11
11
  const fileName = basename(filePath);
@@ -43,8 +43,8 @@ export async function uploadFile(filePath) {
43
43
  }
44
44
  export async function uploadFromUrl(sourceUrl, fileName) {
45
45
  const headers = getAuthHeaders();
46
- if (!headers["Authorization"] && !headers["X-API-TOKEN"]) {
47
- throw new Error("Not authenticated. Use login or set_api_token first.");
46
+ if (!headers["Authorization"] && !headers["X-API-TOKEN"] && !headers["X-MCP-TOKEN"]) {
47
+ throw new Error("Not authenticated. Use login, set_api_token, or set_mcp_token first.");
48
48
  }
49
49
  // Fetch the file from the URL
50
50
  const fetchRes = await fetch(sourceUrl);
@@ -1 +1 @@
1
- {"version":3,"file":"upload.js","sourceRoot":"","sources":["../../src/tools/upload.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"upload.js","sourceRoot":"","sources":["../../src/tools/upload.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@estation/create-cms-site",
3
- "version": "2.5.1",
3
+ "version": "2.6.0",
4
4
  "description": "MCP server to scaffold Next.js sites powered by eSTATION CMS",
5
5
  "type": "module",
6
6
  "bin": {
@@ -25,7 +25,7 @@ export default async function DynamicPage({ params }: PageProps) {
25
25
  const data = await getPageBySlug(slug, locale);
26
26
  const orderedBlocks = getOrderedBlocks(data.page.blocks, data.blocks);
27
27
 
28
- return <SectionRenderer blocks={orderedBlocks} />;
28
+ return <SectionRenderer blocks={orderedBlocks} locale={locale} />;
29
29
  }
30
30
 
31
31
  function getOrderedBlocks(
@@ -14,8 +14,8 @@ export async function generateMetadata({ params }: PageProps): Promise<Metadata>
14
14
  const block = findBlockBySlug(blocks, slug);
15
15
  if (!block) return { title: "Post Not Found" };
16
16
  return {
17
- title: str(block.content.title, block.name),
18
- description: str(block.content.excerpt),
17
+ title: str(block.content.title, locale, block.name),
18
+ description: str(block.content.excerpt, locale),
19
19
  };
20
20
  }
21
21
 
@@ -26,11 +26,11 @@ export default async function BlogDetailPage({ params }: PageProps) {
26
26
 
27
27
  if (!block) notFound();
28
28
 
29
- const title = str(block.content.title, block.name);
30
- const author = str(block.content.author);
31
- const date = str(block.content.publishDate) || block.created_at;
32
- const featuredImage = str(block.content.featuredImage);
33
- const content = str(block.content.content);
29
+ const title = str(block.content.title, locale, block.name);
30
+ const author = str(block.content.author, locale);
31
+ const date = str(block.content.publishDate, locale) || block.created_at;
32
+ const featuredImage = str(block.content.featuredImage, locale);
33
+ const content = str(block.content.content, locale);
34
34
 
35
35
  return (
36
36
  <article className="py-16 px-6">
@@ -22,8 +22,8 @@ export default async function BlogListingPage({ params }: PageProps) {
22
22
  }
23
23
 
24
24
  const sorted = [...blocks].sort((a, b) => {
25
- const da = str(a.content.publishDate) || a.created_at;
26
- const db = str(b.content.publishDate) || b.created_at;
25
+ const da = str(a.content.publishDate, locale) || a.created_at;
26
+ const db = str(b.content.publishDate, locale) || b.created_at;
27
27
  return new Date(db).getTime() - new Date(da).getTime();
28
28
  });
29
29
 
@@ -36,11 +36,11 @@ export default async function BlogListingPage({ params }: PageProps) {
36
36
  ) : (
37
37
  <div className="space-y-8">
38
38
  {sorted.map((block) => {
39
- const slug = str(block.content.slug);
40
- const title = str(block.content.title, block.name);
41
- const excerpt = str(block.content.excerpt);
42
- const featuredImage = str(block.content.featuredImage);
43
- const date = str(block.content.publishDate) || block.created_at;
39
+ const slug = str(block.content.slug, locale);
40
+ const title = str(block.content.title, locale, block.name);
41
+ const excerpt = str(block.content.excerpt, locale);
42
+ const featuredImage = str(block.content.featuredImage, locale);
43
+ const date = str(block.content.publishDate, locale) || block.created_at;
44
44
 
45
45
  return (
46
46
  <article key={block.uuid} className="border-b pb-8">
@@ -20,7 +20,7 @@ export default async function CollectionPage({ params }: PageProps) {
20
20
  {blocks.length === 0 ? (
21
21
  <p className="text-gray-500 text-center">No items in this collection.</p>
22
22
  ) : (
23
- <SectionRenderer blocks={blocks} />
23
+ <SectionRenderer blocks={blocks} locale={locale} />
24
24
  )}
25
25
  </div>
26
26
  </div>
@@ -14,8 +14,8 @@ export async function generateMetadata({ params }: PageProps): Promise<Metadata>
14
14
  const block = findBlockBySlug(blocks, slug);
15
15
  if (!block) return { title: "Event Not Found" };
16
16
  return {
17
- title: str(block.content.title, block.name),
18
- description: str(block.content.description),
17
+ title: str(block.content.title, locale, block.name),
18
+ description: str(block.content.description, locale),
19
19
  };
20
20
  }
21
21
 
@@ -26,13 +26,13 @@ export default async function EventDetailPage({ params }: PageProps) {
26
26
 
27
27
  if (!block) notFound();
28
28
 
29
- const title = str(block.content.title, block.name);
30
- const description = str(block.content.description);
31
- const location = str(block.content.location);
32
- const startDate = str(block.content.startDate);
33
- const endDate = str(block.content.endDate);
34
- const organizer = str(block.content.organizer);
35
- const content = str(block.content.content);
29
+ const title = str(block.content.title, locale, block.name);
30
+ const description = str(block.content.description, locale);
31
+ const location = str(block.content.location, locale);
32
+ const startDate = str(block.content.startDate, locale);
33
+ const endDate = str(block.content.endDate, locale);
34
+ const organizer = str(block.content.organizer, locale);
35
+ const content = str(block.content.content, locale);
36
36
 
37
37
  return (
38
38
  <article className="py-16 px-6">
@@ -22,8 +22,8 @@ export default async function EventsListingPage({ params }: PageProps) {
22
22
  }
23
23
 
24
24
  const sorted = [...blocks].sort((a, b) => {
25
- const da = str(a.content.startDate) || a.created_at;
26
- const db = str(b.content.startDate) || b.created_at;
25
+ const da = str(a.content.startDate, locale) || a.created_at;
26
+ const db = str(b.content.startDate, locale) || b.created_at;
27
27
  return new Date(da).getTime() - new Date(db).getTime();
28
28
  });
29
29
 
@@ -36,12 +36,12 @@ export default async function EventsListingPage({ params }: PageProps) {
36
36
  ) : (
37
37
  <div className="space-y-8">
38
38
  {sorted.map((block) => {
39
- const slug = str(block.content.slug);
40
- const title = str(block.content.title, block.name);
41
- const description = str(block.content.description);
42
- const location = str(block.content.location);
43
- const startDate = str(block.content.startDate);
44
- const endDate = str(block.content.endDate);
39
+ const slug = str(block.content.slug, locale);
40
+ const title = str(block.content.title, locale, block.name);
41
+ const description = str(block.content.description, locale);
42
+ const location = str(block.content.location, locale);
43
+ const startDate = str(block.content.startDate, locale);
44
+ const endDate = str(block.content.endDate, locale);
45
45
 
46
46
  return (
47
47
  <article key={block.uuid} className="border-b pb-8">
@@ -14,8 +14,8 @@ export async function generateMetadata({ params }: PageProps): Promise<Metadata>
14
14
  const block = findBlockBySlug(blocks, slug);
15
15
  if (!block) return { title: "Article Not Found" };
16
16
  return {
17
- title: str(block.content.title, block.name),
18
- description: str(block.content.excerpt),
17
+ title: str(block.content.title, locale, block.name),
18
+ description: str(block.content.excerpt, locale),
19
19
  };
20
20
  }
21
21
 
@@ -26,12 +26,12 @@ export default async function NewsDetailPage({ params }: PageProps) {
26
26
 
27
27
  if (!block) notFound();
28
28
 
29
- const title = str(block.content.title, block.name);
30
- const author = str(block.content.author);
31
- const category = str(block.content.category);
32
- const date = str(block.content.publishDate) || block.created_at;
33
- const featuredImage = str(block.content.featuredImage);
34
- const content = str(block.content.content);
29
+ const title = str(block.content.title, locale, block.name);
30
+ const author = str(block.content.author, locale);
31
+ const category = str(block.content.category, locale);
32
+ const date = str(block.content.publishDate, locale) || block.created_at;
33
+ const featuredImage = str(block.content.featuredImage, locale);
34
+ const content = str(block.content.content, locale);
35
35
 
36
36
  return (
37
37
  <article className="py-16 px-6">
@@ -22,8 +22,8 @@ export default async function NewsListingPage({ params }: PageProps) {
22
22
  }
23
23
 
24
24
  const sorted = [...blocks].sort((a, b) => {
25
- const da = str(a.content.publishDate) || a.created_at;
26
- const db = str(b.content.publishDate) || b.created_at;
25
+ const da = str(a.content.publishDate, locale) || a.created_at;
26
+ const db = str(b.content.publishDate, locale) || b.created_at;
27
27
  return new Date(db).getTime() - new Date(da).getTime();
28
28
  });
29
29
 
@@ -36,11 +36,11 @@ export default async function NewsListingPage({ params }: PageProps) {
36
36
  ) : (
37
37
  <div className="space-y-8">
38
38
  {sorted.map((block) => {
39
- const slug = str(block.content.slug);
40
- const title = str(block.content.title, block.name);
41
- const excerpt = str(block.content.excerpt);
42
- const category = str(block.content.category);
43
- const date = str(block.content.publishDate) || block.created_at;
39
+ const slug = str(block.content.slug, locale);
40
+ const title = str(block.content.title, locale, block.name);
41
+ const excerpt = str(block.content.excerpt, locale);
42
+ const category = str(block.content.category, locale);
43
+ const date = str(block.content.publishDate, locale) || block.created_at;
44
44
 
45
45
  return (
46
46
  <article key={block.uuid} className="border-b pb-8">
@@ -25,7 +25,7 @@ export default async function Home({ params }: PageProps) {
25
25
  try {
26
26
  const data = await getPageBySlug("index", locale);
27
27
  const orderedBlocks = getOrderedBlocks(data.page.blocks, data.blocks);
28
- return <SectionRenderer blocks={orderedBlocks} />;
28
+ return <SectionRenderer blocks={orderedBlocks} locale={locale} />;
29
29
  } catch {
30
30
  return (
31
31
  <div className="py-20 px-6 text-center">
@@ -26,7 +26,7 @@ const SECTION_MAP: Record<string, React.FC<SectionProps>> = {
26
26
  form: ContactSection,
27
27
  };
28
28
 
29
- export function SectionRenderer({ blocks }: { blocks: ContentBlock[] }) {
29
+ export function SectionRenderer({ blocks, locale }: { blocks: ContentBlock[]; locale?: string }) {
30
30
  return (
31
31
  <>
32
32
  {blocks.map((block) => {
@@ -34,7 +34,7 @@ export function SectionRenderer({ blocks }: { blocks: ContentBlock[] }) {
34
34
  const Component = SECTION_MAP[tag] || GenericSection;
35
35
  return (
36
36
  <section key={block.uuid} data-cms-block={tag}>
37
- <Component block={block} />
37
+ <Component block={block} locale={locale} />
38
38
  </section>
39
39
  );
40
40
  })}
@@ -1,16 +1,16 @@
1
1
  import type { SectionProps } from "@/lib/types";
2
2
  import { str } from "@/lib/types";
3
3
 
4
- export function CTASection({ block }: SectionProps) {
4
+ export function CTASection({ block, locale }: SectionProps) {
5
5
  const c = block.content;
6
- const description = str(c.description);
7
- const buttonText = str(c.buttonText);
6
+ const description = str(c.description, locale);
7
+ const buttonText = str(c.buttonText, locale);
8
8
 
9
9
  return (
10
10
  <div className="py-20 px-6 bg-gray-900 text-white text-center">
11
11
  <div className="max-w-2xl mx-auto">
12
12
  <h2 data-cms-field="title" className="text-3xl font-bold mb-4">
13
- {str(c.title, "Ready to get started?")}
13
+ {str(c.title, locale, "Ready to get started?")}
14
14
  </h2>
15
15
  {description && (
16
16
  <p data-cms-field="description" className="text-lg text-gray-300 mb-8">
@@ -20,7 +20,7 @@ export function CTASection({ block }: SectionProps) {
20
20
  {buttonText && (
21
21
  <a
22
22
  data-cms-field="buttonText"
23
- href={str(c.buttonLink, "#")}
23
+ href={str(c.buttonLink, locale, "#")}
24
24
  className="inline-block px-8 py-3 bg-white text-gray-900 rounded-lg font-medium hover:bg-gray-100 transition-colors"
25
25
  >
26
26
  {buttonText}
@@ -1,13 +1,13 @@
1
1
  import type { SectionProps } from "@/lib/types";
2
2
  import { str } from "@/lib/types";
3
3
 
4
- export function ContactSection({ block }: SectionProps) {
4
+ export function ContactSection({ block, locale }: SectionProps) {
5
5
  const c = block.content;
6
- const title = str(c.title, "Contact Us");
7
- const description = str(c.description);
8
- const email = str(c.email);
9
- const phone = str(c.phone);
10
- const address = str(c.address);
6
+ const title = str(c.title, locale, "Contact Us");
7
+ const description = str(c.description, locale);
8
+ const email = str(c.email, locale);
9
+ const phone = str(c.phone, locale);
10
+ const address = str(c.address, locale);
11
11
 
12
12
  return (
13
13
  <div className="py-16 px-6">
@@ -1,9 +1,9 @@
1
1
  import type { SectionProps } from "@/lib/types";
2
2
  import { str } from "@/lib/types";
3
3
 
4
- export function FAQSection({ block }: SectionProps) {
4
+ export function FAQSection({ block, locale }: SectionProps) {
5
5
  const c = block.content;
6
- const title = str(c.title);
6
+ const title = str(c.title, locale);
7
7
 
8
8
  const faqs: { question: string; answer: string }[] = [];
9
9
  if (Array.isArray(c.items?.fieldValue)) {
@@ -1,10 +1,10 @@
1
1
  import type { SectionProps } from "@/lib/types";
2
2
  import { str } from "@/lib/types";
3
3
 
4
- export function FeaturesSection({ block }: SectionProps) {
4
+ export function FeaturesSection({ block, locale }: SectionProps) {
5
5
  const c = block.content;
6
- const title = str(c.title);
7
- const description = str(c.description);
6
+ const title = str(c.title, locale);
7
+ const description = str(c.description, locale);
8
8
 
9
9
  const features: { title: string; description: string }[] = [];
10
10
  if (Array.isArray(c.items?.fieldValue)) {
@@ -1,9 +1,9 @@
1
1
  import type { SectionProps, ListItem } from "@/lib/types";
2
2
  import { str } from "@/lib/types";
3
3
 
4
- export function GallerySection({ block }: SectionProps) {
4
+ export function GallerySection({ block, locale }: SectionProps) {
5
5
  const c = block.content;
6
- const title = str(c.title);
6
+ const title = str(c.title, locale);
7
7
  const items = (Array.isArray(c.items?.fieldValue) ? c.items.fieldValue : []) as ListItem[];
8
8
 
9
9
  return (
@@ -1,6 +1,6 @@
1
1
  import type { SectionProps } from "@/lib/types";
2
2
 
3
- export function GenericSection({ block }: SectionProps) {
3
+ export function GenericSection({ block, locale }: SectionProps) {
4
4
  const c = block.content;
5
5
  const entries = Object.entries(c).filter(
6
6
  ([, field]) => field?.fieldValue !== undefined && field?.fieldValue !== null && field?.fieldValue !== ""
@@ -1,17 +1,17 @@
1
1
  import type { SectionProps } from "@/lib/types";
2
2
  import { str } from "@/lib/types";
3
3
 
4
- export function HeroSection({ block }: SectionProps) {
4
+ export function HeroSection({ block, locale }: SectionProps) {
5
5
  const c = block.content;
6
- const buttonText = str(c.buttonText);
6
+ const buttonText = str(c.buttonText, locale);
7
7
 
8
8
  return (
9
9
  <div className="py-20 px-6 text-center">
10
10
  <h1 data-cms-field="header" className="text-4xl md:text-5xl font-bold tracking-tight">
11
- {str(c.header, "Hero Title")}
11
+ {str(c.header, locale, "Hero Title")}
12
12
  </h1>
13
13
  <p data-cms-field="description" className="mt-4 text-lg text-gray-600 max-w-2xl mx-auto">
14
- {str(c.description, "Hero description goes here.")}
14
+ {str(c.description, locale, "Hero description goes here.")}
15
15
  </p>
16
16
  {buttonText && (
17
17
  <a
@@ -4,7 +4,7 @@ import { useState } from "react";
4
4
  import type { SectionProps, ListItem } from "@/lib/types";
5
5
  import { str } from "@/lib/types";
6
6
 
7
- export function SliderSection({ block }: SectionProps) {
7
+ export function SliderSection({ block, locale }: SectionProps) {
8
8
  const c = block.content;
9
9
  const items = (Array.isArray(c.items?.fieldValue) ? c.items.fieldValue : []) as ListItem[];
10
10
  const [current, setCurrent] = useState(0);
@@ -1,9 +1,9 @@
1
1
  import type { SectionProps } from "@/lib/types";
2
2
  import { str } from "@/lib/types";
3
3
 
4
- export function TestimonialsSection({ block }: SectionProps) {
4
+ export function TestimonialsSection({ block, locale }: SectionProps) {
5
5
  const c = block.content;
6
- const title = str(c.title);
6
+ const title = str(c.title, locale);
7
7
 
8
8
  const testimonials: { name: string; quote: string; role?: string }[] = [];
9
9
  if (Array.isArray(c.items?.fieldValue)) {
@@ -1,11 +1,11 @@
1
1
  import type { SectionProps } from "@/lib/types";
2
2
  import { str } from "@/lib/types";
3
3
 
4
- export function TextSection({ block }: SectionProps) {
4
+ export function TextSection({ block, locale }: SectionProps) {
5
5
  const c = block.content;
6
- const title = str(c.title);
7
- const subtitle = str(c.subtitle);
8
- const body = str(c.body);
6
+ const title = str(c.title, locale);
7
+ const subtitle = str(c.subtitle, locale);
8
+ const body = str(c.body, locale);
9
9
 
10
10
  return (
11
11
  <div className="py-16 px-6 max-w-3xl mx-auto">
@@ -1,12 +1,43 @@
1
1
  export interface ContentField {
2
2
  fieldType: string;
3
- fieldValue: string | ListItem[];
3
+ fieldValue: string | Record<string, string> | ListItem[];
4
4
  }
5
5
 
6
- /** Safely extract a string value from a content field. */
7
- export function str(field: ContentField | undefined, fallback = ""): string {
8
- if (!field || typeof field.fieldValue !== "string") return fallback;
9
- return field.fieldValue || fallback;
6
+ // Known locale codes for detecting locale vs fallback string
7
+ const LOCALE_CODE_SET = new Set(["en","es","fr","de","zh","ja","ko","pt","ar","ku","tr","it","ru","hi","fa","he","ur","nl","sv","pl","th","vi","id","ms","fil","bn","ta","te","ml","kn"]);
8
+
9
+ /**
10
+ * Safely extract a string value from a content field.
11
+ * Handles both plain strings and multilingual locale maps.
12
+ *
13
+ * Usage:
14
+ * str(field) → returns default locale value
15
+ * str(field, "en") → returns English value
16
+ * str(field, "ar") → returns Arabic value
17
+ * str(field, "ar", "fallback") → returns Arabic or "fallback"
18
+ * str(field, "Fallback Text") → returns value or "Fallback Text" (backwards compat)
19
+ */
20
+ export function str(field: ContentField | undefined, localeOrFallback?: string, fallback = ""): string {
21
+ if (!field) return fallback || localeOrFallback || "";
22
+
23
+ const fv = field.fieldValue;
24
+
25
+ // Determine if second arg is a locale code or a fallback string
26
+ const isLocale = localeOrFallback ? LOCALE_CODE_SET.has(localeOrFallback) : false;
27
+ const locale = isLocale ? localeOrFallback : undefined;
28
+ const fb = isLocale ? fallback : (localeOrFallback || fallback);
29
+
30
+ // Plain string (old format or non-translatable field)
31
+ if (typeof fv === "string") return fv || fb;
32
+
33
+ // Multilingual locale map: { en: "Hello", ar: "مرحبا" }
34
+ if (fv && typeof fv === "object" && !Array.isArray(fv)) {
35
+ const localeMap = fv as Record<string, string>;
36
+ if (locale && localeMap[locale]) return localeMap[locale];
37
+ return localeMap["en"] || Object.values(localeMap)[0] || fb;
38
+ }
39
+
40
+ return fb;
10
41
  }
11
42
 
12
43
  /** Safely extract a list value from a content field. */
@@ -94,6 +125,7 @@ export interface PageWithBlocksResponse {
94
125
 
95
126
  export interface SectionProps {
96
127
  block: ContentBlock;
128
+ locale?: string;
97
129
  className?: string;
98
130
  }
99
131