Construindo um blog no FireCMS Cloud

Não precisamos explicar os benefícios de usar um headless CMS em vez de uma abordagem de blog tradicional como o WordPress, mas aqui estão alguns:
- é mais fácil e rápido de desenvolver, pois não há acoplamento entre frontend e backend.
- como seu frontend é independente, você é livre para alterá-lo da forma que quiser, o mesmo vale para o backend.
- é adequado para aplicações omnicanal, você pode usar o mesmo backend e CMS com múltiplos apps e sites
- equipes menores e especializadas
- escalabilidade
- menos custos
- flexibilidade e simplicidade
Vamos construir um blog com FireCMS
Seção intitulada “Vamos construir um blog com FireCMS”Vamos construir uma coleção que hospeda posts de blog. Cada um dos posts do blog incluirá um array dinâmico de elementos.
O FireCMS tem um recurso integrado que permite construir arrays dinâmicos. A
propriedade de array pode ser configurada com a prop oneOf, para conter objetos
que contêm um tipo específico de valor. Isso é perfeito para construir nossa estrutura de dados de entrada de blog!
Esses são os tipos que usaremos:
- Definimos o tipo da coleção de blog:
type BlogEntry = { name: string, header_image: string, created_on: Date, status: string, content: (BlogEntryImages | BlogEntryText | BlogEntryProducts)[];}- E cada um dos tipos do array
content:
type BlogEntryImages = { type: "images"; value: string[];}
type BlogEntryText = { type: "text"; value: string;}
type BlogEntryProducts = { type: "products"; value: object[]; // Usamos um objeto genérico aqui, mas sinta-se à vontade para definir um tipo para seus produtos}Criar a coleção
Seção intitulada “Criar a coleção”Vamos começar inicializando nossa coleção, sem nenhuma propriedade:
export const blogCollection = buildCollection<BlogEntry>({ name: "Blog entry", path: "blog", properties: {}});Propriedades básicas
Seção intitulada “Propriedades básicas”Em seguida vamos adicionar algumas propriedades simples para nossas entradas.
- Queremos ter um título, que deve sempre estar definido, então definimos a prop required em validation como true:
buildProperty({ name: "Title", validation: { required: true }, dataType: "string"})- Uma imagem que ficará no topo do post do blog:
buildProperty({ name: "Header image", dataType: "string", storage: { storagePath: "images", acceptedFiles: ["image/*"], metadata: { cacheControl: "max-age=1000000" } }})- e uma data “created on” que é autogerada quando o documento é criado.
buildProperty( { name: "Created on", dataType: "date", autoValue: "on_create"})Campo de status condicional
Seção intitulada “Campo de status condicional”Agora queremos adicionar uma propriedade string status que terá dois valores possíveis:
published e draft. Só queremos permitir o estado
published quando o restante dos campos estiver correto.
Neste caso vamos manter simples, e apenas verificaremos se a imagem de cabeçalho está definida:
buildProperty(({ values }) => ({ name: "Status", validation: { required: true }, dataType: "string", columnWidth: 140, enumValues: { published: { id: "published", label: "Published", disabled: !values.header_image, }, draft: "Draft" }}))Conteúdo da entrada do blog
Seção intitulada “Conteúdo da entrada do blog”O conteúdo das nossas entradas de blog precisa ser dinâmico, para que os gestores de conteúdo possam criar entradas complexas com diferentes componentes.
O conteúdo será um array de objetos, que terá um atributo type (que
funciona como discriminador) e um atributo value.
Definiremos 3 tipos:
images: um array de imagenstext: um campo de texto Markdownproducts: um array de referências para outra coleção,productsneste caso.
Usamos a prop oneOf em propriedades de array, que é projetada exatamente para este
caso de uso. Você só precisa definir:
buildProperty({ name: "Content", description: "Exemplo de um array complexo com múltiplas propriedades como filhos", validation: { required: true }, dataType: "array", columnWidth: 400, oneOf: { typeField: "type", // você pode omitir as props `typeField` e `valueField` para usar os padrões valueField: "value", properties: { images: buildProperty({ name: "Images", dataType: "array", of: buildProperty({ dataType: "string", storage: { storagePath: "images", acceptedFiles: ["image/*"], metadata: { cacheControl: "max-age=1000000" } } }), description: "Este campo permite fazer upload de múltiplas imagens de uma vez e reordenar" }), text: buildProperty({ dataType: "string", name: "Text", markdown: true }), products: buildProperty({ name: "Products", dataType: "array", of: { dataType: "reference", path: "products" // você precisa definir uma coleção válida neste caminho } }) } }})Esta configuração de array criará objetos no datasource com o formato:
{ // ... content: [ { "type": "text", "value": "Óculos escuros ou óculos de sol são uma forma de proteção ocular..." }, { "type": "images", "value": [ "images/photo-1511499767150-a48a237f0083.jpeg", "images/photo-1577803645773-f96470509666.jpeg" ] } ], // ...}Criando uma view de prévia
Seção intitulada “Criando uma view de prévia”Vamos usar outro recurso do FireCMS: views personalizadas para entidades!
O FireCMS permite adicionar views adicionais às suas views de entidade, que são definidas como componentes React. As props que você recebe para construir este componente são a coleção da entidade, a entidade original e os valores modificados.
Neste caso, vamos criar alguns componentes React para representar nossa entrada de blog
como o app frontend faria. Este é o mesmo código que você poderia
usar em qualquer framework SSR usando React, como next.js.
Você também poderia ter uma configuração mais complexa que envia seus dados para seu app SSR através de uma API e renderiza o resultado.
Você pode encontrar o código para BlogEntryPreview em
BlogEntryPreview.
Quando você tiver criado seu componente, há 2 formas de adicioná-lo à sua coleção:
Registrá-lo no CMS
Seção intitulada “Registrá-lo no CMS”A forma preferida é registrá-lo no CMS, para que esteja disponível no seletor de view da coleção.
Para isso, você precisa adicioná-lo à prop entityViews da exportação do config principal do app FireCMS:
import { FireCMSAppConfig } from "@firecms/cloud";const appConfig: FireCMSAppConfig = { version: "1", collections: [], entityViews: [ { key: "blog_preview", name: "Preview", Builder: BlogEntryPreview } ],}Registrá-lo na coleção
Seção intitulada “Registrá-lo na coleção”Se você estiver definindo sua coleção em código, também pode registrá-lo na própria coleção:
import {buildCollection} from "@firecms/core";export const blogCollection = buildCollection<BlogEntry>({ name: "Blog entry", path: "blog", entityViews: [ { key: "blog_preview", name: "Preview", Builder: BlogEntryPreview } ], properties: { // ... }});Código completo:
Seção intitulada “Código completo:”Se juntarmos todas as partes que construímos neste tutorial, obtemos o seguinte código para a coleção de blog:
import { buildCollection, buildProperty } from "@firecms/core";import { BlogEntryPreview } from "./BlogEntryPreview";import { BlogEntry } from "./types";
export const blogCollection = buildCollection<BlogEntry>({ name: "Blog entry", id: "blog", path: "blog", entityViews: [{ key: "preview", name: "Preview", Builder: BlogEntryPreview }], properties: { name: buildProperty({ name: "Name", validation: { required: true }, dataType: "string" }), header_image: buildProperty({ name: "Header image", dataType: "string", storage: { mediaType: "image", storagePath: "images", acceptedFiles: ["image/*"], metadata: { cacheControl: "max-age=1000000" } } }), content: buildProperty({ name: "Content", description: "Example of a complex array with multiple properties as children", validation: { required: true }, dataType: "array", columnWidth: 400, oneOf: { typeField: "type", // you can ommit these `typeField` and `valueField` props to use the defaults valueField: "value", properties: { images: buildProperty({ name: "Images", dataType: "array", of: buildProperty({ dataType: "string", storage: { mediaType: "image", storagePath: "images", acceptedFiles: ["image/*"], metadata: { cacheControl: "max-age=1000000" } } }), description: "This fields allows uploading multiple images at once and reordering" }), text: buildProperty({ dataType: "string", name: "Text", markdown: true }), products: buildProperty({ name: "Products", dataType: "array", of: { dataType: "reference", path: "products" // you need to define a valid collection in this path } }) } } }), status: buildProperty(({ values }) => ({ name: "Status", validation: { required: true }, dataType: "string", columnWidth: 140, enumValues: { published: { id: "published", label: "Published", disabled: !values.header_image }, draft: "Draft" }, defaultValue: "draft" })), created_on: buildProperty({ name: "Created on", dataType: "date", autoValue: "on_create" }) }})