System bloków

Zrozumienie architektury wielokrotnego użytku bloków Cmssy

Przegląd

Bloki to podstawowe jednostki budulcowe każdej strony w Cmssy. Każdy blok to samodzielny komponent UI, który można umieszczać, konfigurować i wykorzystywać ponownie na różnych stronach. System bloków opiera się na czterech kluczowych koncepcjach:

  • Schemat - Definicje pól opisujące edytowalną treść (tekst, obrazy, linki, repeatery)
  • Domyślna treść - Wstępnie wypełnione wartości dla nowych instancji bloku
  • Komponent - Komponent React, który renderuje blok na stronie
  • Style - Opcjonalne CSS (klasy Tailwind działają od razu)

Interfejs definicji bloku

Każdy blok opisuje interfejs BlockDefinition:

interface BlockDefinition {
  type: string;              // Unikalny identyfikator (np. 'hero')
  name: string;              // Nazwa wyświetlana w edytorze
  description: string;       // Krótki opis
  category: string;          // Kategoria do grupowania
  icon: string;              // Ikona na pasku bocznym
  schema: SchemaField[];     // Definicje edytowalnych pól
  defaultContent: object;    // Domyślne wartości dla nowych instancji
  componentUrl: string;      // URL skompilowanego pakietu JS
  cssUrl?: string;           // Opcjonalny URL pakietu CSS
}

Struktura instancji bloku

Gdy blok jest umieszczony na stronie, staje się instancją bloku z unikalną treścią:

{
  id: string;        // Unikalny UUID dla tej instancji
  type: string;      // Typ bloku (np. 'hero')
  content: {         // Treść według języka
    en: {
      heading: "Build faster",
      subheading: "Ship with confidence"
    },
    pl: {
      heading: "Buduj szybciej",
      subheading: "Dostarczaj z pewnością"
    }
  }
}

PlatformContext

Każdy komponent bloku otrzymuje prop PlatformContext z informacjami o środowisku uruchomieniowym:

interface PlatformContext {
  language: string;          // Aktualny język ('en', 'pl')
  isEditor: boolean;         // True podczas renderowania w edytorze admina
  isPreview: boolean;        // True w trybie podglądu edytora
  workspace: {               // Info o aktualnym workspace
    id: string;
    slug: string;
  };
  siteConfig: SiteConfig;    // Ustawienia całej witryny
  navigation: NavItem[];     // Struktura nawigacji witryny
  apiBaseUrl: string;        // URL backendu API
  localizeHref: (href: string) => string;  // Dodaje prefiks języka do URL
  pages?: {                  // Dane kolekcji (dla bloków kolekcji)
    items: PageItem[];       // Tablica stron
    total: number;
    hasMore: boolean;
  };
}

Użyj context.language aby renderować odpowiednią wersję językową. Użyj context.isEditor aby wyłączyć interaktywne elementy (formularze, modale) podczas edycji. Użyj context.localizeHref aby generować linki z prefiksem języka.

Bloki kolekcji

Bloki kolekcji mogą dynamicznie listować strony CMS - idealne do listy postów na blogu, siatki produktów lub dowolnej treści pochodzącej z typów stron.

Jak to działa

  1. Dodaj pole pageSelector do schematu bloku (np. parentPage)
  2. Platforma wykrywa to pole i pobiera strony podrzędne po stronie serwera (SSR)
  3. Dane stron są automatycznie wstrzykiwane do context.pages
  4. Twój blok renderuje listę z context.pages.items

Interfejs PageItem

interface PageItem {
  id: string;
  slug: string;
  fullSlug: string;
  publishedAt: string | null;
  displayName: Record<string, string>;
  seoTitle: Record<string, string> | null;
  seoDescription: Record<string, string> | null;
  customFields: Array<{ fieldKey: string; value: unknown }>;
  pageType: string;
}

Dostęp do pól niestandardowych

function getCustomField(item: PageItem, key: string): unknown {
  const field = item.customFields?.find((f) => f.fieldKey === key);
  return field?.value ?? null;
}

// Użycie
const coverImage = getCustomField(item, "cover_image") as string | null;
const author = getCustomField(item, "author") as string | null;

Funkcje po stronie klienta

Bloki kolekcji mogą dodawać wyszukiwanie, filtrowanie i nieskończone przewijanie po stronie klienta. Użyj context.workspace.id i endpointu /api/graphql aby pobrać więcej stron:

const res = await fetch('/api/graphql', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    query: `query PublicPagesByType($workspaceId: String!, $parentSlug: String, $search: String, $limit: Int, $offset: Int) {
      publicPagesByType(workspaceId: $workspaceId, parentSlug: $parentSlug, search: $search, limit: $limit, offset: $offset) {
        items { id slug fullSlug publishedAt displayName seoDescription customFields pageType }
        total hasMore
      }
    }`,
    variables: { workspaceId: context.workspace.id, parentSlug, search, limit, offset }
  })
});

Wbudowana biblioteka bloków

Cmssy zawiera rosnącą kolekcję gotowych bloków w kategoriach takich jak Marketing (hero, cennik, FAQ, formularze kontaktowe), Dokumentacja (artykuły, bloki kodu, przewodniki krok po kroku) i Layout (nagłówek, stopka). Przeglądaj i instaluj je z Biblioteki Designu w panelu admina - bez pisania kodu.

Własne bloki

Możesz tworzyć własne bloki na trzy sposoby:

  1. AI Block Builder - Opisz czego chcesz w języku naturalnym, a AI wygeneruje kompletny blok (komponent + schemat + style). Dostępne w panelu admina w Bloki > Utwórz z AI.
  2. Import CLI - Buduj bloki lokalnie z React/Tailwind i importuj je używając cmssy import. CLI pakuje kod, wgrywa na blob storage i rejestruje blok w Twoim workspace.
  3. Biblioteka designu - Przeglądaj i instaluj gotowe bloki z biblioteki Cmssy. Instalacja jednym kliknięciem dodaje blok do Twojego workspace'u.

Cykl życia bloku

  1. Definicja - Typ bloku jest rejestrowany w workspace ze schematem i skompilowanym komponentem
  2. Instancjacja - Użytkownik przeciąga blok na stronę; tworzona jest instancja z domyślną treścią i unikalnym UUID
  3. Edycja - Użytkownik edytuje treść przez panel admina; schemat definiuje interfejs edycji
  4. Publikacja - Strona jest publikowana; treść bloku jest zamrażana jako publishedBlocks
  5. Renderowanie - Frontend ładuje komponent bloku z blob URL; renderuje z treścią i PlatformContext

Typy pól schematu

TypOpisTyp wartościPrzykład
singleLineKrótkie pole tekstowestringNagłówek, tekst przycisku
multiLineTextarea na dłuższą treśćstringOpis, akapit
richTextEdytor WYSIWYG z formatowaniemstringTreść artykułu, bio
numericPole numerycznenumberCena, ilość
dateSelektor datystringData publikacji
mediaUpload obrazu/wideostringObraz hero, avatar
linkPole URL (wewnętrzny/zewnętrzny)stringURL przycisku, link społecznościowy
selectDropdownstringWariant layoutu, motyw
multiselectWielokrotny wybórstring[]Tagi, kategorie
booleanPrzełącznik prawda/fałszbooleanPokaż/ukryj sekcję
colorSelektor kolorustringKolor akcentu
repeaterTablica obiektów z podpolamiRecord<string, unknown>[]Lista funkcji, elementy FAQ
formPicker formularzy - osadź formularz z workspacestringFormularz kontaktowy, newsletter
emailTemplatePicker szablonów e-mailstringE-mail potwierdzający
emailConfigurationPicker konfiguracji e-mailstringUstawienia SMTP
pageSelectorWybór stron z workspaceArray<PageRef>Strona nadrzędna dla kolekcji

Page Selector i PageRef

Typ pola pageSelector pozwala blokom odwoływać się do innych stron w workspace. Wybrane strony są zapisywane jako obiekty PageRef zawierające slug i zlokalizowane nazwy wyświetlane:

interface PageRef {
  slug: string;                          // Slug strony (np. '/about')
  displayName: Record<string, string>;   // Zlokalizowane nazwy { en: 'About', pl: 'O nas' }
}

W połączeniu z blokami kolekcji, pole pageSelector określa stronę, której podstrony mają być wyświetlone. Platforma automatycznie pobiera i wstrzykuje dane do context.pages.

Pola warunkowe

Użyj showWhen aby warunkowo pokazywać pola na podstawie wartości innych pól:

showButton: {
  type: "boolean",
  label: "Pokaż przycisk CTA",
  defaultValue: true,
},
buttonText: {
  type: "singleLine",
  label: "Tekst przycisku",
  showWhen: { field: "showButton", equals: true },
}

Internacjonalizacja (i18n)

Treść bloku jest przechowywana per język. Każdy klucz językowy zawiera pełen zestaw pól:

{
  "content": {
    "en": { "heading": "Hello World" },
    "pl": { "heading": "Witaj Świecie" }
  }
}

Edytor zawiera przełącznik języka do edycji treści w każdym włączonym języku. Treść w domyślnym języku jest zawsze wymagana; dodatkowe języki są opcjonalne.

Następne kroki