Skip to content

W7F-FB/website

Repository files navigation

W7F Website

This project uses Prismic CMS for content management alongside a Next.js frontend.

Commit Conventions

This project follows Conventional Commits specification for commit messages. All commits should use the format:

<type>[optional scope]: <description>

[optional body]

[optional footer(s)]

Common types include:

  • feat: - new features
  • fix: - bug fixes
  • docs: - documentation updates
  • style: - formatting changes
  • refactor: - code refactoring
  • test: - adding tests
  • chore: - maintenance tasks

Content Management System (CMS)

This project uses Prismic CMS for headless content management, providing a powerful and flexible backend for managing website content.

Architecture

  • Prismic Repository: Cloud-based content management at world-sevens-football.prismic.io
  • Frontend: Next.js application that fetches content from Prismic API
  • Content Delivery: Real-time content updates via Prismic's CDN
  • Slice Machine: Local development tool for managing content types and slices

Key Features

  • Custom Types: Define flexible content models for different content types
  • Slice Machine: Local development tool for creating and managing slices
  • Rich Text: Advanced rich text editing with embedded content
  • Preview System: Full-website previews of draft content before publishing
  • Image Optimization: Automatic image transformations and CDN delivery
  • Releases: Group changes across multiple documents for coordinated publishing

Development Workflow

  1. Schema Definition: Define Custom Types and Slices using Slice Machine at http://localhost:9999
  2. Content Creation: Use Prismic dashboard to create and manage content
  3. Frontend Integration: Query content using Prismic SDK in Next.js components
  4. Preview & Test: Use preview functionality to see changes before publishing

Content Types

  • Blog: News articles and blog posts with rich text content
  • Tournament: Tournament information with navigation settings and images
  • Policy: Legal documents with rich text content and optional PDF files
  • Team Member: Staff profiles with departments and display ordering
  • Website: Site-wide navigation and footer configuration
  • Image With Text: Content blocks combining images with rich text descriptions

Documentation & Resources

Prismic TypeScript Types

This project uses the official Prismic approach for TypeScript types:

  • Types File: prismicio-types.d.ts (auto-generated by Slice Machine)
  • Generator: @slicemachine/adapter-next
  • Standard: Follows Prismic Next.js starter

Import Pattern

import type { BlogDocument, PolicyDocument, TournamentDocument } from "../../../prismicio-types"

Why This Approach?

  • Official Standard: Recommended by Prismic for Next.js projects
  • Slice Machine Integration: Automatically generates types when you modify content models
  • No Conflicts: Single source of truth for all Prismic types
  • Always Current: Types update automatically when content models change

Image Optimization

Prismic provides automatic image optimization through their CDN. Use the image field directly:

import { PrismicNextImage } from "@prismicio/next"

// In your component
<PrismicNextImage 
  field={document.data.image}
  width={800}
  height={600}
  className="rounded-lg"
/>

This automatically provides responsive images, format optimization (WebP/AVIF), and CDN delivery.

Component Conventions

All components in this project follow consistent patterns for maintainability and type safety:

Structure

  • Import React and utilities at the top
  • Use React.forwardRef for proper ref forwarding
  • Accept React.ComponentProps<"element"> for full HTML element support
  • Destructure className and spread remaining props

Props Type Declarations

  • If a component has no additional props: Declare props type inline, don't create a separate type/interface
  • If a component has additional props: Create a separate interface that extends the base props
  • Use React.ComponentProps<"element"> or React.HTMLAttributes<HTMLElement> as base types

Styling

  • Use the cn utility from @/lib/utils for class merging
  • Always accept and merge className prop with base styles

Examples

Component with no additional props (inline declaration):

import * as React from "react"
import { cn } from "@/lib/utils"

const MyComponent = React.forwardRef<
  HTMLDivElement,
  React.ComponentProps<"div">
>(({ className, ...props }, ref) => {
  return (
    <div
      ref={ref}
      className={cn("base-styles", className)}
      {...props}
    />
  )
})

MyComponent.displayName = "MyComponent"

Component with additional props (separate interface):

import * as React from "react"
import { cn } from "@/lib/utils"

interface MyComponentProps extends React.ComponentProps<"div"> {
  variant?: "primary" | "secondary"
  size?: "sm" | "md" | "lg"
}

const MyComponent = React.forwardRef<HTMLDivElement, MyComponentProps>(
  ({ className, variant = "primary", size = "md", ...props }, ref) => {
    return (
      <div
        ref={ref}
        className={cn("base-styles", variant, size, className)}
        {...props}
      />
    )
  }
)

MyComponent.displayName = "MyComponent"

Exports

  • Use named exports in curly braces
  • Set displayName for better debugging

Prismic CMS Integration

This project uses Prismic CMS with Slice Machine for flexible content management. Follow these conventions when implementing new content types and components:

Architecture Overview

┌─────────────────────────┐     ┌─────────────────────┐
│   Prismic Dashboard     │     │   Next.js App       │
│                         │     │                     │
│  - Content editing      │────▶│  - Server components│
│  - Media management     │     │  - Static generation│
│  - Preview mode         │     │  - API integration  │
└─────────────────────────┘     └─────────────────────┘
                │                          │
                └──────────┬───────────────┘
                           │
                    ┌──────▼──────┐
                    │ Slice Machine│
                    │  - Local dev │
                    │  - Type gen  │
                    └─────────────┘

1. Custom Type Design Conventions

When creating Custom Types in Slice Machine:

{
  "Main": {
    "title": {
      "type": "Text",
      "config": {
        "label": "Title",
        "placeholder": "Enter title"
      }
    },
    "description": {
      "type": "RichText",
      "config": {
        "label": "Description",
        "placeholder": "Enter description",
        "allowTargetBlank": true,
        "multi": "paragraph,preformatted,heading1,heading2,heading3,heading4,heading5,heading6,strong,em,hyperlink,image,embed,list-item,o-list-item,rtl"
      }
    },
    "image": {
      "type": "Image",
      "config": {
        "label": "Image",
        "constraint": {
          "width": 1200,
          "height": 800
        },
        "thumbnails": []
      }
    }
  }
}

Key Requirements:

  • Use appropriate field types for content structure
  • Configure constraints for images and text fields
  • Set up proper labels and placeholders for content editors
  • Use RichText for formatted content, Text for simple strings

2. Component Structure Pattern

Follow this pattern for Prismic content components:

a) Server Component (Data Fetching)

// app/[...]/page.tsx or components/[feature]/[name].tsx
import { createClient } from "@/prismicio"
import type { BlogDocument } from "../../../prismicio-types"

export async function BlogPage({ params }: { params: { uid: string } }) {
  const client = createClient()
  const blog = await client.getByUID("blog", params.uid)
  
  if (!blog) {
    notFound()
  }

  return (
    <Container>
      <h1>{blog.data.title}</h1>
      <PrismicRichText field={blog.data.content} />
      {blog.data.image && (
        <PrismicNextImage field={blog.data.image} />
      )}
    </Container>
  )
}

b) CMS Query Functions

// cms/queries/[content-type].ts
import { createClient } from "../../prismicio"
import type { BlogDocument } from "../../../prismicio-types"
import * as prismic from "@prismicio/client"

/**
 * Get a single blog by UID (slug)
 */
export async function getBlogBySlug(uid: string): Promise<BlogDocument | null> {
  try {
    const client = createClient()
    return await client.getByUID("blog", uid)
  } catch (error) {
    if (error instanceof Error && "status" in error && (error as { status: number }).status === 404) {
      return null
    }
    throw error
  }
}

/**
 * Get all blogs ordered by date
 */
export async function getAllBlogs(): Promise<BlogDocument[]> {
  try {
    const client = createClient()
    return await client.getAllByType("blog", {
      orderings: [
        { field: "my.blog.date", direction: "desc" },
        { field: "my.blog.title", direction: "asc" },
      ],
    })
  } catch (error) {
    if (error instanceof Error && error.message.includes("No documents were returned")) {
      return []
    }
    throw error
  }
}

3. Prismic Rich Text Integration

Use PrismicRichText component for formatted content:

import { PrismicRichText } from "@prismicio/react"
import { H1, H2, H3, P } from "@/components/website-base/typography"

const components = {
  heading1: ({ children }) => <H1>{children}</H1>,
  heading2: ({ children }) => <H2>{children}</H2>,
  heading3: ({ children }) => <H3>{children}</H3>,
  paragraph: ({ children }) => <P>{children}</P>,
  hyperlink: ({ node, children }) => (
    <a 
      href={node.data.url || ""} 
      className="underline underline-offset-2"
      {...(node.data.link_type === "Web" ? { target: "_blank", rel: "noopener noreferrer" } : {})}
    >
      {children}
    </a>
  ),
}

// In your component
<PrismicRichText 
  field={document.data.content} 
  components={components}
/>

4. Error Handling Patterns

Implement robust error handling for API calls:

// cms/queries/[content-type].ts
export async function getContentWithFallback(): Promise<ContentDocument[]> {
  try {
    const client = createClient()
    return await client.getAllByType("content", {
      orderings: [{ field: "my.content.date", direction: "desc" }],
    })
  } catch (error) {
    console.error("Error fetching content:", error)
    // Return empty array for graceful degradation
    return []
  }
}

// For build resilience, use Promise.allSettled
export async function getMultipleContentTypes() {
  const [blogs, tournaments, policies] = await Promise.allSettled([
    getAllBlogs(),
    getTournaments(),
    getPolicies()
  ]).then(results => results.map(result => 
    result.status === 'fulfilled' ? result.value : []
  ))
  
  return { blogs, tournaments, policies }
}

5. Slice Machine Configuration

Configure Slice Machine for local development:

// slicemachine.config.json
{
  "apiEndpoint": "https://your-repo-name.cdn.prismic.io/api/v2",
  "repositoryName": "your-repo-name",
  "adapter": "@slicemachine/adapter-next",
  "libraries": ["./src/cms/slices"],
  "localSliceSimulatorURL": "http://localhost:3000/slice-simulator"
}

Start Slice Machine for content modeling:

npm run slicemachine

6. Best Practices

  1. Server Components: Use server components for data fetching and static content
  2. Type Safety: Always use generated Prismic types from prismicio-types.d.ts
  3. Error Handling: Implement graceful fallbacks for API failures
  4. Image Optimization: Use PrismicNextImage for automatic optimization
  5. Rich Text: Use PrismicRichText with custom components for consistent styling
  6. Caching: Leverage Next.js caching with appropriate revalidation strategies

7. Preview and Development

  1. Preview Mode: Access draft content via Prismic's preview system
  2. Slice Simulator: Test slices in isolation at /slice-simulator
  3. Local Development:
    npm run dev          # Start Next.js
    npm run slicemachine  # Start Slice Machine (separate terminal)

8. Common Patterns

Pattern 1: Document by UID

const document = await client.getByUID("blog", params.slug)

Pattern 2: All Documents of Type

const documents = await client.getAllByType("blog", {
  orderings: [{ field: "my.blog.date", direction: "desc" }]
})

Pattern 3: Filtered Documents

const documents = await client.getAllByType("team_member", {
  filters: [prismic.filter.at("my.team_member.department", "Leadership")]
})

About

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors