feat: complete agency site build (Phases 1-7)

Full Next.js 16 + Payload CMS 3.x agency site with:
- Homepage: Hero, TrustBar, Services, Configurator wizard, Process,
  Selected Works, Philosophy, CTA Banner
- Sub-pages: /services (3 pillars + AI Layer), /work/[slug] (case
  studies), /about (philosophy + story)
- Configurator: 3-step wizard with AI brief generation API
- i18n: Full EN/FR bilingual with next-intl
- Design system: Cormorant Garamond + Inter, celestial blue palette,
  glassmorphism nav, Framer Motion animations
- Payload CMS collections: Projects, Services, Submissions, Media

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-25 20:37:38 +01:00
commit a1f9eca76c
64 changed files with 15810 additions and 0 deletions

View File

@@ -0,0 +1,20 @@
import type { CollectionConfig } from 'payload'
export const Media: CollectionConfig = {
slug: 'media',
access: {
read: () => true,
},
upload: {
staticDir: 'public/media',
mimeTypes: ['image/*', 'application/pdf'],
},
fields: [
{
name: 'alt',
type: 'text',
localized: true,
required: true,
},
],
}

View File

@@ -0,0 +1,80 @@
import type { CollectionConfig } from 'payload'
export const Projects: CollectionConfig = {
slug: 'projects',
admin: {
useAsTitle: 'title',
},
access: {
read: () => true,
},
fields: [
{
name: 'title',
type: 'text',
required: true,
localized: true,
},
{
name: 'slug',
type: 'text',
required: true,
unique: true,
admin: {
position: 'sidebar',
},
},
{
name: 'description',
type: 'textarea',
required: true,
localized: true,
},
{
name: 'tags',
type: 'array',
fields: [
{
name: 'tag',
type: 'text',
},
],
},
{
name: 'featured',
type: 'checkbox',
defaultValue: false,
admin: {
position: 'sidebar',
},
},
{
name: 'comingSoon',
type: 'checkbox',
defaultValue: false,
admin: {
position: 'sidebar',
},
},
{
name: 'thumbnail',
type: 'upload',
relationTo: 'media',
},
{
name: 'content',
type: 'richText',
localized: true,
},
{
name: 'techStack',
type: 'array',
fields: [
{
name: 'technology',
type: 'text',
},
],
},
],
}

View File

@@ -0,0 +1,49 @@
import type { CollectionConfig } from 'payload'
export const Services: CollectionConfig = {
slug: 'services',
admin: {
useAsTitle: 'title',
},
access: {
read: () => true,
},
fields: [
{
name: 'title',
type: 'text',
required: true,
localized: true,
},
{
name: 'slug',
type: 'text',
required: true,
unique: true,
},
{
name: 'description',
type: 'textarea',
required: true,
localized: true,
},
{
name: 'features',
type: 'array',
localized: true,
fields: [
{
name: 'feature',
type: 'text',
},
],
},
{
name: 'order',
type: 'number',
admin: {
position: 'sidebar',
},
},
],
}

View File

@@ -0,0 +1,87 @@
import type { CollectionConfig } from 'payload'
export const Submissions: CollectionConfig = {
slug: 'submissions',
admin: {
useAsTitle: 'name',
},
fields: [
{
name: 'name',
type: 'text',
required: true,
},
{
name: 'company',
type: 'text',
},
{
name: 'email',
type: 'email',
required: true,
},
{
name: 'services',
type: 'select',
hasMany: true,
options: [
{ label: 'Web Design & Development', value: 'web' },
{ label: 'Custom Systems', value: 'systems' },
{ label: 'Digital Infrastructure', value: 'infrastructure' },
],
},
{
name: 'aiEnhancement',
type: 'checkbox',
defaultValue: false,
},
{
name: 'aiType',
type: 'select',
options: [
{ label: 'AI Teammate', value: 'teammate' },
{ label: 'Customer-Facing AI', value: 'customer-facing' },
{ label: 'Data Intelligence', value: 'data-intelligence' },
{ label: 'Not Sure Yet', value: 'unsure' },
],
admin: {
condition: (data) => data?.aiEnhancement,
},
},
{
name: 'industry',
type: 'select',
options: [
{ label: 'Maritime / Yachting', value: 'maritime' },
{ label: 'Hospitality', value: 'hospitality' },
{ label: 'Technology', value: 'technology' },
{ label: 'Real Estate', value: 'real-estate' },
{ label: 'Finance', value: 'finance' },
{ label: 'NGO / Nonprofit', value: 'ngo' },
{ label: 'Other', value: 'other' },
],
},
{
name: 'scope',
type: 'textarea',
},
{
name: 'timeline',
type: 'select',
options: [
{ label: 'ASAP', value: 'asap' },
{ label: '1-3 months', value: '1-3-months' },
{ label: '3-6 months', value: '3-6-months' },
{ label: 'Just exploring', value: 'exploring' },
],
},
{
name: 'brief',
type: 'textarea',
admin: {
readOnly: true,
description: 'AI-generated project brief',
},
},
],
}

View File

@@ -0,0 +1,15 @@
import type { CollectionConfig } from 'payload'
export const Users: CollectionConfig = {
slug: 'users',
auth: true,
admin: {
useAsTitle: 'email',
},
fields: [
{
name: 'name',
type: 'text',
},
],
}

View File

@@ -0,0 +1,44 @@
import { buildConfig } from 'payload'
import { postgresAdapter } from '@payloadcms/db-postgres'
import { lexicalEditor } from '@payloadcms/richtext-lexical'
import sharp from 'sharp'
import path from 'path'
import { fileURLToPath } from 'url'
import { Projects } from './collections/Projects'
import { Services } from './collections/Services'
import { Submissions } from './collections/Submissions'
import { Media } from './collections/Media'
import { Users } from './collections/Users'
const filename = fileURLToPath(import.meta.url)
const dirname = path.dirname(filename)
export default buildConfig({
admin: {
user: Users.slug,
importMap: {
baseDir: path.resolve(dirname, '..'),
},
},
collections: [Users, Media, Projects, Services, Submissions],
editor: lexicalEditor(),
secret: process.env.PAYLOAD_SECRET || 'CHANGE-ME-IN-PRODUCTION',
typescript: {
outputFile: path.resolve(dirname, '../payload-types.ts'),
},
db: postgresAdapter({
pool: {
connectionString: process.env.DATABASE_URI || 'postgresql://postgres:postgres@localhost:5432/letsbe',
},
}),
sharp,
localization: {
locales: [
{ label: 'English', code: 'en' },
{ label: 'Français', code: 'fr' },
],
defaultLocale: 'en',
fallback: true,
},
})