feat: enhance OGB with components from Daemez
Day 1 Phase 1 Complete: Core Infrastructure ✅ Enhanced Header: - Added CTA button (enabled by default, personal CTA text) - Added Announcement Bar (dismissible, with type variants) - Organized in tabs for better UX - Localized fields for multilingual support ✅ Enhanced Footer: - Added Social Links (LinkedIn, Twitter, GitHub, YouTube, etc.) - Added EU Funding Logos (MANDATORY for Kit Digital grant) - Added Personal Info (name, professional title, location, email, phone) - Added Newsletter section (optional) - Organized in tabs for better UX ✅ Enhanced Categories: - Added description field (localized, for SEO) - Added color field (hex color for UI badges) - Added order field (custom sort) - Added type field (project/post/skill categories) - Added icon field for visual identification ✅ Localization: - Configured EN/ES/CA (English, Spanish, Catalan) - Default locale: English - Fallback enabled ✅ Projects Collection: - Portfolio showcase collection - Content: heroImage, short/full description, problem/solution/results - Technical: techStack, highlights - Media: gallery, liveUrl, githubUrl, videoUrl, caseStudyUrl - Metadata: client, year, duration, role, teamSize - Categories relationship (filtered to project categories) - Related projects relationship - SEO plugin integration - Draft/publish workflow with schedule - Featured flag for homepage display ✅ Plugins Updated: - Search plugin: now includes 'projects' collection - Redirects plugin: now includes 'projects' collection ✅ Shared Components: - Created /components/RowLabel.tsx (generic, reusable) ✅ TypeScript Types: - Generated payload-types.ts for all new fields All changes adapted for personal branding (not B2B corporate). Ready for testing and content creation.
This commit is contained in:
+159
-13
@@ -10,20 +10,166 @@ export const Footer: GlobalConfig = {
|
|||||||
},
|
},
|
||||||
fields: [
|
fields: [
|
||||||
{
|
{
|
||||||
name: 'navItems',
|
type: 'tabs',
|
||||||
type: 'array',
|
tabs: [
|
||||||
fields: [
|
{
|
||||||
link({
|
label: 'Navigation',
|
||||||
appearances: false,
|
fields: [
|
||||||
}),
|
{
|
||||||
],
|
name: 'navItems',
|
||||||
maxRows: 6,
|
type: 'array',
|
||||||
admin: {
|
label: 'Footer Links',
|
||||||
initCollapsed: true,
|
fields: [
|
||||||
components: {
|
link({
|
||||||
RowLabel: '@/Footer/RowLabel#RowLabel',
|
appearances: false,
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
maxRows: 12,
|
||||||
|
admin: {
|
||||||
|
description: 'Main footer navigation links (Projects, Blog, About, Contact, Privacy, etc.)',
|
||||||
|
initCollapsed: true,
|
||||||
|
components: {
|
||||||
|
RowLabel: '@/Footer/RowLabel#RowLabel',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'socialLinks',
|
||||||
|
type: 'array',
|
||||||
|
label: 'Social Links',
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
name: 'platform',
|
||||||
|
type: 'select',
|
||||||
|
required: true,
|
||||||
|
options: [
|
||||||
|
{ label: 'LinkedIn', value: 'linkedin' },
|
||||||
|
{ label: 'Twitter / X', value: 'twitter' },
|
||||||
|
{ label: 'GitHub', value: 'github' },
|
||||||
|
{ label: 'YouTube', value: 'youtube' },
|
||||||
|
{ label: 'Facebook', value: 'facebook' },
|
||||||
|
{ label: 'Instagram', value: 'instagram' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'url',
|
||||||
|
type: 'text',
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
admin: {
|
||||||
|
description: 'Social media links',
|
||||||
|
initCollapsed: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
},
|
{
|
||||||
|
label: 'Branding & Compliance',
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
name: 'euFundingLogos',
|
||||||
|
type: 'array',
|
||||||
|
label: 'EU Funding Logos',
|
||||||
|
required: true,
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
name: 'logo',
|
||||||
|
type: 'upload',
|
||||||
|
relationTo: 'media',
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'alt',
|
||||||
|
type: 'text',
|
||||||
|
required: true,
|
||||||
|
admin: {
|
||||||
|
description: 'Alt text (e.g., "Financiado por la Unión Europea - NextGenerationEU")',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'link',
|
||||||
|
type: 'text',
|
||||||
|
admin: {
|
||||||
|
description: 'Optional link URL',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
admin: {
|
||||||
|
description: 'MANDATORY for Kit Digital grant: EU, Next Generation EU, Plan de Recuperación logos',
|
||||||
|
initCollapsed: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'personalInfo',
|
||||||
|
type: 'group',
|
||||||
|
label: 'Personal Information',
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
name: 'name',
|
||||||
|
type: 'text',
|
||||||
|
defaultValue: 'Oscar Gimenez',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'professionalTitle',
|
||||||
|
type: 'text',
|
||||||
|
localized: true,
|
||||||
|
admin: {
|
||||||
|
description: 'Professional title or tagline (e.g., "Full Stack Developer", "Tech Consultant")',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'location',
|
||||||
|
type: 'text',
|
||||||
|
localized: true,
|
||||||
|
admin: {
|
||||||
|
description: 'Location (e.g., "Barcelona, Spain")',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'email',
|
||||||
|
type: 'email',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'phone',
|
||||||
|
type: 'text',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Newsletter',
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
name: 'newsletterEnabled',
|
||||||
|
type: 'checkbox',
|
||||||
|
defaultValue: false,
|
||||||
|
admin: {
|
||||||
|
description: 'Enable newsletter signup in footer',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'newsletterHeading',
|
||||||
|
type: 'text',
|
||||||
|
localized: true,
|
||||||
|
admin: {
|
||||||
|
description: 'Newsletter section heading',
|
||||||
|
condition: (data) => data.newsletterEnabled === true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'newsletterDescription',
|
||||||
|
type: 'textarea',
|
||||||
|
localized: true,
|
||||||
|
admin: {
|
||||||
|
description: 'Newsletter description text',
|
||||||
|
condition: (data) => data.newsletterEnabled === true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
hooks: {
|
hooks: {
|
||||||
|
|||||||
+141
-13
@@ -10,20 +10,148 @@ export const Header: GlobalConfig = {
|
|||||||
},
|
},
|
||||||
fields: [
|
fields: [
|
||||||
{
|
{
|
||||||
name: 'navItems',
|
type: 'tabs',
|
||||||
type: 'array',
|
tabs: [
|
||||||
fields: [
|
{
|
||||||
link({
|
label: 'Navigation',
|
||||||
appearances: false,
|
fields: [
|
||||||
}),
|
{
|
||||||
],
|
name: 'navItems',
|
||||||
maxRows: 6,
|
type: 'array',
|
||||||
admin: {
|
label: 'Main Navigation',
|
||||||
initCollapsed: true,
|
fields: [
|
||||||
components: {
|
link({
|
||||||
RowLabel: '@/Header/RowLabel#RowLabel',
|
appearances: false,
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
maxRows: 6,
|
||||||
|
admin: {
|
||||||
|
description: 'Main navigation menu items',
|
||||||
|
initCollapsed: true,
|
||||||
|
components: {
|
||||||
|
RowLabel: '@/Header/RowLabel#RowLabel',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'ctaButton',
|
||||||
|
type: 'group',
|
||||||
|
label: 'CTA Button',
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
name: 'enabled',
|
||||||
|
type: 'checkbox',
|
||||||
|
defaultValue: true,
|
||||||
|
admin: {
|
||||||
|
description: 'Show CTA button in header',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'text',
|
||||||
|
type: 'text',
|
||||||
|
localized: true,
|
||||||
|
admin: {
|
||||||
|
description: 'Button text (e.g., "Get in Touch", "Hire Me", "Let\'s Talk")',
|
||||||
|
condition: (data) => data.ctaButton?.enabled === true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'link',
|
||||||
|
type: 'text',
|
||||||
|
admin: {
|
||||||
|
description: 'Button URL or page slug',
|
||||||
|
condition: (data) => data.ctaButton?.enabled === true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'style',
|
||||||
|
type: 'select',
|
||||||
|
options: [
|
||||||
|
{ label: 'Primary', value: 'primary' },
|
||||||
|
{ label: 'Secondary', value: 'secondary' },
|
||||||
|
{ label: 'Outline', value: 'outline' },
|
||||||
|
],
|
||||||
|
defaultValue: 'primary',
|
||||||
|
admin: {
|
||||||
|
description: 'Button visual style',
|
||||||
|
condition: (data) => data.ctaButton?.enabled === true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
},
|
{
|
||||||
|
label: 'Announcement Bar',
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
name: 'announcementBar',
|
||||||
|
type: 'group',
|
||||||
|
label: 'Announcement Bar',
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
name: 'enabled',
|
||||||
|
type: 'checkbox',
|
||||||
|
defaultValue: false,
|
||||||
|
admin: {
|
||||||
|
description: 'Show announcement bar above header',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'message',
|
||||||
|
type: 'text',
|
||||||
|
localized: true,
|
||||||
|
admin: {
|
||||||
|
description: 'Announcement text',
|
||||||
|
condition: (data) => data.announcementBar?.enabled === true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'link',
|
||||||
|
type: 'text',
|
||||||
|
admin: {
|
||||||
|
description: 'Optional link URL',
|
||||||
|
condition: (data) => data.announcementBar?.enabled === true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'linkText',
|
||||||
|
type: 'text',
|
||||||
|
localized: true,
|
||||||
|
admin: {
|
||||||
|
description: 'Link text (e.g., "Learn more")',
|
||||||
|
condition: (data) => data.announcementBar?.enabled === true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'type',
|
||||||
|
type: 'select',
|
||||||
|
options: [
|
||||||
|
{ label: 'Info', value: 'info' },
|
||||||
|
{ label: 'Success', value: 'success' },
|
||||||
|
{ label: 'Warning', value: 'warning' },
|
||||||
|
{ label: 'Promotion', value: 'promotion' },
|
||||||
|
],
|
||||||
|
defaultValue: 'info',
|
||||||
|
admin: {
|
||||||
|
description: 'Bar style/color',
|
||||||
|
condition: (data) => data.announcementBar?.enabled === true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'dismissible',
|
||||||
|
type: 'checkbox',
|
||||||
|
defaultValue: true,
|
||||||
|
admin: {
|
||||||
|
description: 'Allow users to close the announcement',
|
||||||
|
condition: (data) => data.announcementBar?.enabled === true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
hooks: {
|
hooks: {
|
||||||
|
|||||||
@@ -14,15 +14,63 @@ export const Categories: CollectionConfig = {
|
|||||||
},
|
},
|
||||||
admin: {
|
admin: {
|
||||||
useAsTitle: 'title',
|
useAsTitle: 'title',
|
||||||
|
defaultColumns: ['title', 'type', 'icon', 'updatedAt'],
|
||||||
},
|
},
|
||||||
fields: [
|
fields: [
|
||||||
{
|
{
|
||||||
name: 'title',
|
name: 'title',
|
||||||
type: 'text',
|
type: 'text',
|
||||||
required: true,
|
required: true,
|
||||||
|
localized: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'description',
|
||||||
|
type: 'textarea',
|
||||||
|
localized: true,
|
||||||
|
admin: {
|
||||||
|
description: 'Category description for archive pages and SEO',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
slugField({
|
slugField({
|
||||||
position: undefined,
|
position: undefined,
|
||||||
}),
|
}),
|
||||||
|
{
|
||||||
|
name: 'type',
|
||||||
|
type: 'select',
|
||||||
|
required: true,
|
||||||
|
options: [
|
||||||
|
{ label: 'Project Category', value: 'project' },
|
||||||
|
{ label: 'Post Category', value: 'post' },
|
||||||
|
{ label: 'Skill Category', value: 'skill' },
|
||||||
|
],
|
||||||
|
admin: {
|
||||||
|
position: 'sidebar',
|
||||||
|
description: 'What type of content this category is for',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'icon',
|
||||||
|
type: 'text',
|
||||||
|
admin: {
|
||||||
|
position: 'sidebar',
|
||||||
|
description: 'Icon name (e.g., code, shield, cloud)',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'color',
|
||||||
|
type: 'text',
|
||||||
|
admin: {
|
||||||
|
position: 'sidebar',
|
||||||
|
description: 'Hex color for UI badges/tags (e.g., #3B82F6)',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'order',
|
||||||
|
type: 'number',
|
||||||
|
admin: {
|
||||||
|
position: 'sidebar',
|
||||||
|
description: 'Custom sort order (lower numbers appear first)',
|
||||||
|
},
|
||||||
|
},
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,402 @@
|
|||||||
|
import type { CollectionConfig } from 'payload'
|
||||||
|
import { authenticated } from '../access/authenticated'
|
||||||
|
import { authenticatedOrPublished } from '../access/authenticatedOrPublished'
|
||||||
|
import { slugField } from 'payload'
|
||||||
|
import { populatePublishedAt } from '../hooks/populatePublishedAt'
|
||||||
|
import { generatePreviewPath } from '../utilities/generatePreviewPath'
|
||||||
|
|
||||||
|
import {
|
||||||
|
MetaDescriptionField,
|
||||||
|
MetaImageField,
|
||||||
|
MetaTitleField,
|
||||||
|
OverviewField,
|
||||||
|
PreviewField,
|
||||||
|
} from '@payloadcms/plugin-seo/fields'
|
||||||
|
|
||||||
|
export const Projects: CollectionConfig = {
|
||||||
|
slug: 'projects',
|
||||||
|
labels: {
|
||||||
|
singular: 'Project',
|
||||||
|
plural: 'Projects',
|
||||||
|
},
|
||||||
|
access: {
|
||||||
|
create: authenticated,
|
||||||
|
delete: authenticated,
|
||||||
|
read: authenticatedOrPublished,
|
||||||
|
update: authenticated,
|
||||||
|
},
|
||||||
|
defaultPopulate: {
|
||||||
|
name: true,
|
||||||
|
slug: true,
|
||||||
|
categories: true,
|
||||||
|
meta: {
|
||||||
|
image: true,
|
||||||
|
description: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
admin: {
|
||||||
|
useAsTitle: 'name',
|
||||||
|
defaultColumns: ['name', 'client', 'year', 'featured', 'updatedAt'],
|
||||||
|
livePreview: {
|
||||||
|
url: ({ data, req }) =>
|
||||||
|
generatePreviewPath({
|
||||||
|
slug: data?.slug,
|
||||||
|
collection: 'projects',
|
||||||
|
req,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
preview: (data, { req }) =>
|
||||||
|
generatePreviewPath({
|
||||||
|
slug: data?.slug as string,
|
||||||
|
collection: 'projects',
|
||||||
|
req,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
name: 'name',
|
||||||
|
type: 'text',
|
||||||
|
required: true,
|
||||||
|
localized: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'tabs',
|
||||||
|
tabs: [
|
||||||
|
{
|
||||||
|
label: 'Content',
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
name: 'heroImage',
|
||||||
|
type: 'upload',
|
||||||
|
relationTo: 'media',
|
||||||
|
required: true,
|
||||||
|
admin: {
|
||||||
|
description: 'Main project image/screenshot',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'shortDescription',
|
||||||
|
type: 'textarea',
|
||||||
|
required: true,
|
||||||
|
localized: true,
|
||||||
|
admin: {
|
||||||
|
description: 'Brief description for cards and previews (max 200 chars)',
|
||||||
|
},
|
||||||
|
validate: (val: string | null | undefined) => {
|
||||||
|
if (val && val.length > 200) {
|
||||||
|
return 'Short description must be 200 characters or less'
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'description',
|
||||||
|
type: 'richText',
|
||||||
|
required: true,
|
||||||
|
localized: true,
|
||||||
|
admin: {
|
||||||
|
description: 'Full project description and details',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'problem',
|
||||||
|
type: 'richText',
|
||||||
|
localized: true,
|
||||||
|
admin: {
|
||||||
|
description: 'Problem statement: What challenge did this project solve?',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'solution',
|
||||||
|
type: 'richText',
|
||||||
|
localized: true,
|
||||||
|
admin: {
|
||||||
|
description: 'Solution: How did you solve it?',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'results',
|
||||||
|
type: 'richText',
|
||||||
|
localized: true,
|
||||||
|
admin: {
|
||||||
|
description: 'Results: What were the outcomes?',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'client',
|
||||||
|
type: 'text',
|
||||||
|
localized: true,
|
||||||
|
admin: {
|
||||||
|
description: 'Client name (optional, can anonymize if needed)',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'year',
|
||||||
|
type: 'number',
|
||||||
|
admin: {
|
||||||
|
description: 'Year of completion',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'duration',
|
||||||
|
type: 'text',
|
||||||
|
localized: true,
|
||||||
|
admin: {
|
||||||
|
description: 'Project duration (e.g., "3 months", "6 weeks")',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'role',
|
||||||
|
type: 'text',
|
||||||
|
localized: true,
|
||||||
|
admin: {
|
||||||
|
description: 'Your role in the project (e.g., "Lead Developer", "Full Stack Developer")',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'teamSize',
|
||||||
|
type: 'text',
|
||||||
|
localized: true,
|
||||||
|
admin: {
|
||||||
|
description: 'Team size (e.g., "Solo project", "Team of 5")',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Technical',
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
name: 'techStack',
|
||||||
|
type: 'array',
|
||||||
|
label: 'Tech Stack',
|
||||||
|
required: true,
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
name: 'name',
|
||||||
|
type: 'text',
|
||||||
|
required: true,
|
||||||
|
admin: {
|
||||||
|
description: 'Technology name (e.g., "React", "Node.js", "PostgreSQL")',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'category',
|
||||||
|
type: 'select',
|
||||||
|
options: [
|
||||||
|
{ label: 'Frontend', value: 'frontend' },
|
||||||
|
{ label: 'Backend', value: 'backend' },
|
||||||
|
{ label: 'Database', value: 'database' },
|
||||||
|
{ label: 'DevOps', value: 'devops' },
|
||||||
|
{ label: 'Tool', value: 'tool' },
|
||||||
|
{ label: 'Other', value: 'other' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
admin: {
|
||||||
|
initCollapsed: true,
|
||||||
|
components: {
|
||||||
|
RowLabel: '@/components/RowLabel#RowLabel',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'highlights',
|
||||||
|
type: 'array',
|
||||||
|
label: 'Technical Highlights',
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
name: 'title',
|
||||||
|
type: 'text',
|
||||||
|
required: true,
|
||||||
|
localized: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'description',
|
||||||
|
type: 'textarea',
|
||||||
|
required: true,
|
||||||
|
localized: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
admin: {
|
||||||
|
description: 'Key technical achievements or interesting challenges solved',
|
||||||
|
initCollapsed: true,
|
||||||
|
components: {
|
||||||
|
RowLabel: '@/components/RowLabel#RowLabel',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Media & Links',
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
name: 'gallery',
|
||||||
|
type: 'array',
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
name: 'image',
|
||||||
|
type: 'upload',
|
||||||
|
relationTo: 'media',
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'caption',
|
||||||
|
type: 'text',
|
||||||
|
localized: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'alt',
|
||||||
|
type: 'text',
|
||||||
|
required: true,
|
||||||
|
localized: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
admin: {
|
||||||
|
description: 'Additional screenshots, diagrams, or images',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'liveUrl',
|
||||||
|
type: 'text',
|
||||||
|
admin: {
|
||||||
|
description: 'Link to live project (if public)',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'githubUrl',
|
||||||
|
type: 'text',
|
||||||
|
admin: {
|
||||||
|
description: 'GitHub repository URL (if public)',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'videoUrl',
|
||||||
|
type: 'text',
|
||||||
|
admin: {
|
||||||
|
description: 'Project demo video URL (YouTube, Vimeo, etc.)',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'caseStudyUrl',
|
||||||
|
type: 'text',
|
||||||
|
admin: {
|
||||||
|
description: 'Link to external case study or blog post',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Relationships',
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
name: 'categories',
|
||||||
|
type: 'relationship',
|
||||||
|
relationTo: 'categories',
|
||||||
|
hasMany: true,
|
||||||
|
required: true,
|
||||||
|
filterOptions: {
|
||||||
|
type: { equals: 'project' },
|
||||||
|
},
|
||||||
|
admin: {
|
||||||
|
description: 'Project categories (can select multiple)',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'relatedProjects',
|
||||||
|
type: 'relationship',
|
||||||
|
relationTo: 'projects',
|
||||||
|
hasMany: true,
|
||||||
|
filterOptions: ({ id }) => ({
|
||||||
|
id: { not_in: [id] },
|
||||||
|
}),
|
||||||
|
admin: {
|
||||||
|
description: 'Similar or related projects',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'meta',
|
||||||
|
label: 'SEO',
|
||||||
|
fields: [
|
||||||
|
OverviewField({
|
||||||
|
titlePath: 'meta.title',
|
||||||
|
descriptionPath: 'meta.description',
|
||||||
|
imagePath: 'meta.image',
|
||||||
|
}),
|
||||||
|
MetaTitleField({
|
||||||
|
hasGenerateFn: true,
|
||||||
|
}),
|
||||||
|
MetaImageField({
|
||||||
|
relationTo: 'media',
|
||||||
|
}),
|
||||||
|
MetaDescriptionField({}),
|
||||||
|
PreviewField({
|
||||||
|
hasGenerateFn: true,
|
||||||
|
titlePath: 'meta.title',
|
||||||
|
descriptionPath: 'meta.description',
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'featured',
|
||||||
|
type: 'checkbox',
|
||||||
|
defaultValue: false,
|
||||||
|
admin: {
|
||||||
|
position: 'sidebar',
|
||||||
|
description: 'Display this project prominently on the homepage',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'status',
|
||||||
|
type: 'select',
|
||||||
|
required: true,
|
||||||
|
defaultValue: 'completed',
|
||||||
|
options: [
|
||||||
|
{ label: 'Completed', value: 'completed' },
|
||||||
|
{ label: 'In Progress', value: 'in-progress' },
|
||||||
|
{ label: 'Archived', value: 'archived' },
|
||||||
|
],
|
||||||
|
admin: {
|
||||||
|
position: 'sidebar',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'publishedAt',
|
||||||
|
type: 'date',
|
||||||
|
admin: {
|
||||||
|
position: 'sidebar',
|
||||||
|
date: {
|
||||||
|
pickerAppearance: 'dayAndTime',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
hooks: {
|
||||||
|
beforeChange: [
|
||||||
|
({ siblingData, value }) => {
|
||||||
|
if (siblingData._status === 'published' && !value) {
|
||||||
|
return new Date()
|
||||||
|
}
|
||||||
|
return value
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
slugField(),
|
||||||
|
],
|
||||||
|
hooks: {
|
||||||
|
beforeChange: [populatePublishedAt],
|
||||||
|
},
|
||||||
|
versions: {
|
||||||
|
drafts: {
|
||||||
|
autosave: {
|
||||||
|
interval: 100,
|
||||||
|
},
|
||||||
|
schedulePublish: true,
|
||||||
|
},
|
||||||
|
maxPerDoc: 50,
|
||||||
|
},
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
'use client'
|
||||||
|
import { useRowLabel } from '@payloadcms/ui'
|
||||||
|
|
||||||
|
export const RowLabel = () => {
|
||||||
|
const { data, rowNumber } = useRowLabel()
|
||||||
|
|
||||||
|
// Try common field names for labels
|
||||||
|
const label = data?.title || data?.name || data?.clientName || data?.label
|
||||||
|
|
||||||
|
return label || `Item ${String(rowNumber).padStart(2, '0')}`
|
||||||
|
}
|
||||||
+502
-69
@@ -69,6 +69,7 @@ export interface Config {
|
|||||||
collections: {
|
collections: {
|
||||||
pages: Page;
|
pages: Page;
|
||||||
posts: Post;
|
posts: Post;
|
||||||
|
projects: Project;
|
||||||
media: Media;
|
media: Media;
|
||||||
categories: Category;
|
categories: Category;
|
||||||
users: User;
|
users: User;
|
||||||
@@ -91,6 +92,7 @@ export interface Config {
|
|||||||
collectionsSelect: {
|
collectionsSelect: {
|
||||||
pages: PagesSelect<false> | PagesSelect<true>;
|
pages: PagesSelect<false> | PagesSelect<true>;
|
||||||
posts: PostsSelect<false> | PostsSelect<true>;
|
posts: PostsSelect<false> | PostsSelect<true>;
|
||||||
|
projects: ProjectsSelect<false> | ProjectsSelect<true>;
|
||||||
media: MediaSelect<false> | MediaSelect<true>;
|
media: MediaSelect<false> | MediaSelect<true>;
|
||||||
categories: CategoriesSelect<false> | CategoriesSelect<true>;
|
categories: CategoriesSelect<false> | CategoriesSelect<true>;
|
||||||
users: UsersSelect<false> | UsersSelect<true>;
|
users: UsersSelect<false> | UsersSelect<true>;
|
||||||
@@ -106,7 +108,7 @@ export interface Config {
|
|||||||
'payload-migrations': PayloadMigrationsSelect<false> | PayloadMigrationsSelect<true>;
|
'payload-migrations': PayloadMigrationsSelect<false> | PayloadMigrationsSelect<true>;
|
||||||
};
|
};
|
||||||
db: {
|
db: {
|
||||||
defaultIDType: string;
|
defaultIDType: number;
|
||||||
};
|
};
|
||||||
globals: {
|
globals: {
|
||||||
header: Header;
|
header: Header;
|
||||||
@@ -116,7 +118,7 @@ export interface Config {
|
|||||||
header: HeaderSelect<false> | HeaderSelect<true>;
|
header: HeaderSelect<false> | HeaderSelect<true>;
|
||||||
footer: FooterSelect<false> | FooterSelect<true>;
|
footer: FooterSelect<false> | FooterSelect<true>;
|
||||||
};
|
};
|
||||||
locale: null;
|
locale: 'en' | 'es' | 'ca';
|
||||||
user: User & {
|
user: User & {
|
||||||
collection: 'users';
|
collection: 'users';
|
||||||
};
|
};
|
||||||
@@ -154,7 +156,7 @@ export interface UserAuthOperations {
|
|||||||
* via the `definition` "pages".
|
* via the `definition` "pages".
|
||||||
*/
|
*/
|
||||||
export interface Page {
|
export interface Page {
|
||||||
id: string;
|
id: number;
|
||||||
title: string;
|
title: string;
|
||||||
hero: {
|
hero: {
|
||||||
type: 'none' | 'highImpact' | 'mediumImpact' | 'lowImpact';
|
type: 'none' | 'highImpact' | 'mediumImpact' | 'lowImpact';
|
||||||
@@ -181,11 +183,11 @@ export interface Page {
|
|||||||
reference?:
|
reference?:
|
||||||
| ({
|
| ({
|
||||||
relationTo: 'pages';
|
relationTo: 'pages';
|
||||||
value: string | Page;
|
value: number | Page;
|
||||||
} | null)
|
} | null)
|
||||||
| ({
|
| ({
|
||||||
relationTo: 'posts';
|
relationTo: 'posts';
|
||||||
value: string | Post;
|
value: number | Post;
|
||||||
} | null);
|
} | null);
|
||||||
url?: string | null;
|
url?: string | null;
|
||||||
label: string;
|
label: string;
|
||||||
@@ -197,7 +199,7 @@ export interface Page {
|
|||||||
id?: string | null;
|
id?: string | null;
|
||||||
}[]
|
}[]
|
||||||
| null;
|
| null;
|
||||||
media?: (string | null) | Media;
|
media?: (number | null) | Media;
|
||||||
};
|
};
|
||||||
layout: (CallToActionBlock | ContentBlock | MediaBlock | ArchiveBlock | FormBlock)[];
|
layout: (CallToActionBlock | ContentBlock | MediaBlock | ArchiveBlock | FormBlock)[];
|
||||||
meta?: {
|
meta?: {
|
||||||
@@ -205,7 +207,7 @@ export interface Page {
|
|||||||
/**
|
/**
|
||||||
* Maximum upload file size: 12MB. Recommended file size for images is <500KB.
|
* Maximum upload file size: 12MB. Recommended file size for images is <500KB.
|
||||||
*/
|
*/
|
||||||
image?: (string | null) | Media;
|
image?: (number | null) | Media;
|
||||||
description?: string | null;
|
description?: string | null;
|
||||||
};
|
};
|
||||||
publishedAt?: string | null;
|
publishedAt?: string | null;
|
||||||
@@ -223,9 +225,9 @@ export interface Page {
|
|||||||
* via the `definition` "posts".
|
* via the `definition` "posts".
|
||||||
*/
|
*/
|
||||||
export interface Post {
|
export interface Post {
|
||||||
id: string;
|
id: number;
|
||||||
title: string;
|
title: string;
|
||||||
heroImage?: (string | null) | Media;
|
heroImage?: (number | null) | Media;
|
||||||
content: {
|
content: {
|
||||||
root: {
|
root: {
|
||||||
type: string;
|
type: string;
|
||||||
@@ -241,18 +243,18 @@ export interface Post {
|
|||||||
};
|
};
|
||||||
[k: string]: unknown;
|
[k: string]: unknown;
|
||||||
};
|
};
|
||||||
relatedPosts?: (string | Post)[] | null;
|
relatedPosts?: (number | Post)[] | null;
|
||||||
categories?: (string | Category)[] | null;
|
categories?: (number | Category)[] | null;
|
||||||
meta?: {
|
meta?: {
|
||||||
title?: string | null;
|
title?: string | null;
|
||||||
/**
|
/**
|
||||||
* Maximum upload file size: 12MB. Recommended file size for images is <500KB.
|
* Maximum upload file size: 12MB. Recommended file size for images is <500KB.
|
||||||
*/
|
*/
|
||||||
image?: (string | null) | Media;
|
image?: (number | null) | Media;
|
||||||
description?: string | null;
|
description?: string | null;
|
||||||
};
|
};
|
||||||
publishedAt?: string | null;
|
publishedAt?: string | null;
|
||||||
authors?: (string | User)[] | null;
|
authors?: (number | User)[] | null;
|
||||||
populatedAuthors?:
|
populatedAuthors?:
|
||||||
| {
|
| {
|
||||||
id?: string | null;
|
id?: string | null;
|
||||||
@@ -273,7 +275,7 @@ export interface Post {
|
|||||||
* via the `definition` "media".
|
* via the `definition` "media".
|
||||||
*/
|
*/
|
||||||
export interface Media {
|
export interface Media {
|
||||||
id: string;
|
id: number;
|
||||||
alt?: string | null;
|
alt?: string | null;
|
||||||
caption?: {
|
caption?: {
|
||||||
root: {
|
root: {
|
||||||
@@ -290,7 +292,7 @@ export interface Media {
|
|||||||
};
|
};
|
||||||
[k: string]: unknown;
|
[k: string]: unknown;
|
||||||
} | null;
|
} | null;
|
||||||
folder?: (string | null) | FolderInterface;
|
folder?: (number | null) | FolderInterface;
|
||||||
updatedAt: string;
|
updatedAt: string;
|
||||||
createdAt: string;
|
createdAt: string;
|
||||||
url?: string | null;
|
url?: string | null;
|
||||||
@@ -366,18 +368,18 @@ export interface Media {
|
|||||||
* via the `definition` "payload-folders".
|
* via the `definition` "payload-folders".
|
||||||
*/
|
*/
|
||||||
export interface FolderInterface {
|
export interface FolderInterface {
|
||||||
id: string;
|
id: number;
|
||||||
name: string;
|
name: string;
|
||||||
folder?: (string | null) | FolderInterface;
|
folder?: (number | null) | FolderInterface;
|
||||||
documentsAndFolders?: {
|
documentsAndFolders?: {
|
||||||
docs?: (
|
docs?: (
|
||||||
| {
|
| {
|
||||||
relationTo?: 'payload-folders';
|
relationTo?: 'payload-folders';
|
||||||
value: string | FolderInterface;
|
value: number | FolderInterface;
|
||||||
}
|
}
|
||||||
| {
|
| {
|
||||||
relationTo?: 'media';
|
relationTo?: 'media';
|
||||||
value: string | Media;
|
value: number | Media;
|
||||||
}
|
}
|
||||||
)[];
|
)[];
|
||||||
hasNextPage?: boolean;
|
hasNextPage?: boolean;
|
||||||
@@ -392,17 +394,37 @@ export interface FolderInterface {
|
|||||||
* via the `definition` "categories".
|
* via the `definition` "categories".
|
||||||
*/
|
*/
|
||||||
export interface Category {
|
export interface Category {
|
||||||
id: string;
|
id: number;
|
||||||
title: string;
|
title: string;
|
||||||
|
/**
|
||||||
|
* Category description for archive pages and SEO
|
||||||
|
*/
|
||||||
|
description?: string | null;
|
||||||
/**
|
/**
|
||||||
* When enabled, the slug will auto-generate from the title field on save and autosave.
|
* When enabled, the slug will auto-generate from the title field on save and autosave.
|
||||||
*/
|
*/
|
||||||
generateSlug?: boolean | null;
|
generateSlug?: boolean | null;
|
||||||
slug: string;
|
slug: string;
|
||||||
parent?: (string | null) | Category;
|
/**
|
||||||
|
* What type of content this category is for
|
||||||
|
*/
|
||||||
|
type: 'project' | 'post' | 'skill';
|
||||||
|
/**
|
||||||
|
* Icon name (e.g., code, shield, cloud)
|
||||||
|
*/
|
||||||
|
icon?: string | null;
|
||||||
|
/**
|
||||||
|
* Hex color for UI badges/tags (e.g., #3B82F6)
|
||||||
|
*/
|
||||||
|
color?: string | null;
|
||||||
|
/**
|
||||||
|
* Custom sort order (lower numbers appear first)
|
||||||
|
*/
|
||||||
|
order?: number | null;
|
||||||
|
parent?: (number | null) | Category;
|
||||||
breadcrumbs?:
|
breadcrumbs?:
|
||||||
| {
|
| {
|
||||||
doc?: (string | null) | Category;
|
doc?: (number | null) | Category;
|
||||||
url?: string | null;
|
url?: string | null;
|
||||||
label?: string | null;
|
label?: string | null;
|
||||||
id?: string | null;
|
id?: string | null;
|
||||||
@@ -416,7 +438,7 @@ export interface Category {
|
|||||||
* via the `definition` "users".
|
* via the `definition` "users".
|
||||||
*/
|
*/
|
||||||
export interface User {
|
export interface User {
|
||||||
id: string;
|
id: number;
|
||||||
name?: string | null;
|
name?: string | null;
|
||||||
updatedAt: string;
|
updatedAt: string;
|
||||||
createdAt: string;
|
createdAt: string;
|
||||||
@@ -464,11 +486,11 @@ export interface CallToActionBlock {
|
|||||||
reference?:
|
reference?:
|
||||||
| ({
|
| ({
|
||||||
relationTo: 'pages';
|
relationTo: 'pages';
|
||||||
value: string | Page;
|
value: number | Page;
|
||||||
} | null)
|
} | null)
|
||||||
| ({
|
| ({
|
||||||
relationTo: 'posts';
|
relationTo: 'posts';
|
||||||
value: string | Post;
|
value: number | Post;
|
||||||
} | null);
|
} | null);
|
||||||
url?: string | null;
|
url?: string | null;
|
||||||
label: string;
|
label: string;
|
||||||
@@ -514,11 +536,11 @@ export interface ContentBlock {
|
|||||||
reference?:
|
reference?:
|
||||||
| ({
|
| ({
|
||||||
relationTo: 'pages';
|
relationTo: 'pages';
|
||||||
value: string | Page;
|
value: number | Page;
|
||||||
} | null)
|
} | null)
|
||||||
| ({
|
| ({
|
||||||
relationTo: 'posts';
|
relationTo: 'posts';
|
||||||
value: string | Post;
|
value: number | Post;
|
||||||
} | null);
|
} | null);
|
||||||
url?: string | null;
|
url?: string | null;
|
||||||
label: string;
|
label: string;
|
||||||
@@ -539,7 +561,7 @@ export interface ContentBlock {
|
|||||||
* via the `definition` "MediaBlock".
|
* via the `definition` "MediaBlock".
|
||||||
*/
|
*/
|
||||||
export interface MediaBlock {
|
export interface MediaBlock {
|
||||||
media: string | Media;
|
media: number | Media;
|
||||||
id?: string | null;
|
id?: string | null;
|
||||||
blockName?: string | null;
|
blockName?: string | null;
|
||||||
blockType: 'mediaBlock';
|
blockType: 'mediaBlock';
|
||||||
@@ -566,12 +588,12 @@ export interface ArchiveBlock {
|
|||||||
} | null;
|
} | null;
|
||||||
populateBy?: ('collection' | 'selection') | null;
|
populateBy?: ('collection' | 'selection') | null;
|
||||||
relationTo?: 'posts' | null;
|
relationTo?: 'posts' | null;
|
||||||
categories?: (string | Category)[] | null;
|
categories?: (number | Category)[] | null;
|
||||||
limit?: number | null;
|
limit?: number | null;
|
||||||
selectedDocs?:
|
selectedDocs?:
|
||||||
| {
|
| {
|
||||||
relationTo: 'posts';
|
relationTo: 'posts';
|
||||||
value: string | Post;
|
value: number | Post;
|
||||||
}[]
|
}[]
|
||||||
| null;
|
| null;
|
||||||
id?: string | null;
|
id?: string | null;
|
||||||
@@ -583,7 +605,7 @@ export interface ArchiveBlock {
|
|||||||
* via the `definition` "FormBlock".
|
* via the `definition` "FormBlock".
|
||||||
*/
|
*/
|
||||||
export interface FormBlock {
|
export interface FormBlock {
|
||||||
form: string | Form;
|
form: number | Form;
|
||||||
enableIntro?: boolean | null;
|
enableIntro?: boolean | null;
|
||||||
introContent?: {
|
introContent?: {
|
||||||
root: {
|
root: {
|
||||||
@@ -609,7 +631,7 @@ export interface FormBlock {
|
|||||||
* via the `definition` "forms".
|
* via the `definition` "forms".
|
||||||
*/
|
*/
|
||||||
export interface Form {
|
export interface Form {
|
||||||
id: string;
|
id: number;
|
||||||
title: string;
|
title: string;
|
||||||
fields?:
|
fields?:
|
||||||
| (
|
| (
|
||||||
@@ -778,12 +800,195 @@ export interface Form {
|
|||||||
updatedAt: string;
|
updatedAt: string;
|
||||||
createdAt: string;
|
createdAt: string;
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* This interface was referenced by `Config`'s JSON-Schema
|
||||||
|
* via the `definition` "projects".
|
||||||
|
*/
|
||||||
|
export interface Project {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
/**
|
||||||
|
* Main project image/screenshot
|
||||||
|
*/
|
||||||
|
heroImage: number | Media;
|
||||||
|
/**
|
||||||
|
* Brief description for cards and previews (max 200 chars)
|
||||||
|
*/
|
||||||
|
shortDescription: string;
|
||||||
|
/**
|
||||||
|
* Full project description and details
|
||||||
|
*/
|
||||||
|
description: {
|
||||||
|
root: {
|
||||||
|
type: string;
|
||||||
|
children: {
|
||||||
|
type: any;
|
||||||
|
version: number;
|
||||||
|
[k: string]: unknown;
|
||||||
|
}[];
|
||||||
|
direction: ('ltr' | 'rtl') | null;
|
||||||
|
format: 'left' | 'start' | 'center' | 'right' | 'end' | 'justify' | '';
|
||||||
|
indent: number;
|
||||||
|
version: number;
|
||||||
|
};
|
||||||
|
[k: string]: unknown;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* Problem statement: What challenge did this project solve?
|
||||||
|
*/
|
||||||
|
problem?: {
|
||||||
|
root: {
|
||||||
|
type: string;
|
||||||
|
children: {
|
||||||
|
type: any;
|
||||||
|
version: number;
|
||||||
|
[k: string]: unknown;
|
||||||
|
}[];
|
||||||
|
direction: ('ltr' | 'rtl') | null;
|
||||||
|
format: 'left' | 'start' | 'center' | 'right' | 'end' | 'justify' | '';
|
||||||
|
indent: number;
|
||||||
|
version: number;
|
||||||
|
};
|
||||||
|
[k: string]: unknown;
|
||||||
|
} | null;
|
||||||
|
/**
|
||||||
|
* Solution: How did you solve it?
|
||||||
|
*/
|
||||||
|
solution?: {
|
||||||
|
root: {
|
||||||
|
type: string;
|
||||||
|
children: {
|
||||||
|
type: any;
|
||||||
|
version: number;
|
||||||
|
[k: string]: unknown;
|
||||||
|
}[];
|
||||||
|
direction: ('ltr' | 'rtl') | null;
|
||||||
|
format: 'left' | 'start' | 'center' | 'right' | 'end' | 'justify' | '';
|
||||||
|
indent: number;
|
||||||
|
version: number;
|
||||||
|
};
|
||||||
|
[k: string]: unknown;
|
||||||
|
} | null;
|
||||||
|
/**
|
||||||
|
* Results: What were the outcomes?
|
||||||
|
*/
|
||||||
|
results?: {
|
||||||
|
root: {
|
||||||
|
type: string;
|
||||||
|
children: {
|
||||||
|
type: any;
|
||||||
|
version: number;
|
||||||
|
[k: string]: unknown;
|
||||||
|
}[];
|
||||||
|
direction: ('ltr' | 'rtl') | null;
|
||||||
|
format: 'left' | 'start' | 'center' | 'right' | 'end' | 'justify' | '';
|
||||||
|
indent: number;
|
||||||
|
version: number;
|
||||||
|
};
|
||||||
|
[k: string]: unknown;
|
||||||
|
} | null;
|
||||||
|
/**
|
||||||
|
* Client name (optional, can anonymize if needed)
|
||||||
|
*/
|
||||||
|
client?: string | null;
|
||||||
|
/**
|
||||||
|
* Year of completion
|
||||||
|
*/
|
||||||
|
year?: number | null;
|
||||||
|
/**
|
||||||
|
* Project duration (e.g., "3 months", "6 weeks")
|
||||||
|
*/
|
||||||
|
duration?: string | null;
|
||||||
|
/**
|
||||||
|
* Your role in the project (e.g., "Lead Developer", "Full Stack Developer")
|
||||||
|
*/
|
||||||
|
role?: string | null;
|
||||||
|
/**
|
||||||
|
* Team size (e.g., "Solo project", "Team of 5")
|
||||||
|
*/
|
||||||
|
teamSize?: string | null;
|
||||||
|
techStack: {
|
||||||
|
/**
|
||||||
|
* Technology name (e.g., "React", "Node.js", "PostgreSQL")
|
||||||
|
*/
|
||||||
|
name: string;
|
||||||
|
category?: ('frontend' | 'backend' | 'database' | 'devops' | 'tool' | 'other') | null;
|
||||||
|
id?: string | null;
|
||||||
|
}[];
|
||||||
|
/**
|
||||||
|
* Key technical achievements or interesting challenges solved
|
||||||
|
*/
|
||||||
|
highlights?:
|
||||||
|
| {
|
||||||
|
title: string;
|
||||||
|
description: string;
|
||||||
|
id?: string | null;
|
||||||
|
}[]
|
||||||
|
| null;
|
||||||
|
/**
|
||||||
|
* Additional screenshots, diagrams, or images
|
||||||
|
*/
|
||||||
|
gallery?:
|
||||||
|
| {
|
||||||
|
image: number | Media;
|
||||||
|
caption?: string | null;
|
||||||
|
alt: string;
|
||||||
|
id?: string | null;
|
||||||
|
}[]
|
||||||
|
| null;
|
||||||
|
/**
|
||||||
|
* Link to live project (if public)
|
||||||
|
*/
|
||||||
|
liveUrl?: string | null;
|
||||||
|
/**
|
||||||
|
* GitHub repository URL (if public)
|
||||||
|
*/
|
||||||
|
githubUrl?: string | null;
|
||||||
|
/**
|
||||||
|
* Project demo video URL (YouTube, Vimeo, etc.)
|
||||||
|
*/
|
||||||
|
videoUrl?: string | null;
|
||||||
|
/**
|
||||||
|
* Link to external case study or blog post
|
||||||
|
*/
|
||||||
|
caseStudyUrl?: string | null;
|
||||||
|
/**
|
||||||
|
* Project categories (can select multiple)
|
||||||
|
*/
|
||||||
|
categories: (number | Category)[];
|
||||||
|
/**
|
||||||
|
* Similar or related projects
|
||||||
|
*/
|
||||||
|
relatedProjects?: (number | Project)[] | null;
|
||||||
|
meta?: {
|
||||||
|
title?: string | null;
|
||||||
|
/**
|
||||||
|
* Maximum upload file size: 12MB. Recommended file size for images is <500KB.
|
||||||
|
*/
|
||||||
|
image?: (number | null) | Media;
|
||||||
|
description?: string | null;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* Display this project prominently on the homepage
|
||||||
|
*/
|
||||||
|
featured?: boolean | null;
|
||||||
|
status: 'completed' | 'in-progress' | 'archived';
|
||||||
|
publishedAt?: string | null;
|
||||||
|
/**
|
||||||
|
* When enabled, the slug will auto-generate from the title field on save and autosave.
|
||||||
|
*/
|
||||||
|
generateSlug?: boolean | null;
|
||||||
|
slug: string;
|
||||||
|
updatedAt: string;
|
||||||
|
createdAt: string;
|
||||||
|
_status?: ('draft' | 'published') | null;
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* This interface was referenced by `Config`'s JSON-Schema
|
* This interface was referenced by `Config`'s JSON-Schema
|
||||||
* via the `definition` "redirects".
|
* via the `definition` "redirects".
|
||||||
*/
|
*/
|
||||||
export interface Redirect {
|
export interface Redirect {
|
||||||
id: string;
|
id: number;
|
||||||
/**
|
/**
|
||||||
* You will need to rebuild the website when changing this field.
|
* You will need to rebuild the website when changing this field.
|
||||||
*/
|
*/
|
||||||
@@ -793,11 +998,15 @@ export interface Redirect {
|
|||||||
reference?:
|
reference?:
|
||||||
| ({
|
| ({
|
||||||
relationTo: 'pages';
|
relationTo: 'pages';
|
||||||
value: string | Page;
|
value: number | Page;
|
||||||
} | null)
|
} | null)
|
||||||
| ({
|
| ({
|
||||||
relationTo: 'posts';
|
relationTo: 'posts';
|
||||||
value: string | Post;
|
value: number | Post;
|
||||||
|
} | null)
|
||||||
|
| ({
|
||||||
|
relationTo: 'projects';
|
||||||
|
value: number | Project;
|
||||||
} | null);
|
} | null);
|
||||||
url?: string | null;
|
url?: string | null;
|
||||||
};
|
};
|
||||||
@@ -809,8 +1018,8 @@ export interface Redirect {
|
|||||||
* via the `definition` "form-submissions".
|
* via the `definition` "form-submissions".
|
||||||
*/
|
*/
|
||||||
export interface FormSubmission {
|
export interface FormSubmission {
|
||||||
id: string;
|
id: number;
|
||||||
form: string | Form;
|
form: number | Form;
|
||||||
submissionData?:
|
submissionData?:
|
||||||
| {
|
| {
|
||||||
field: string;
|
field: string;
|
||||||
@@ -828,18 +1037,23 @@ export interface FormSubmission {
|
|||||||
* via the `definition` "search".
|
* via the `definition` "search".
|
||||||
*/
|
*/
|
||||||
export interface Search {
|
export interface Search {
|
||||||
id: string;
|
id: number;
|
||||||
title?: string | null;
|
title?: string | null;
|
||||||
priority?: number | null;
|
priority?: number | null;
|
||||||
doc: {
|
doc:
|
||||||
relationTo: 'posts';
|
| {
|
||||||
value: string | Post;
|
relationTo: 'posts';
|
||||||
};
|
value: number | Post;
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
relationTo: 'projects';
|
||||||
|
value: number | Project;
|
||||||
|
};
|
||||||
slug?: string | null;
|
slug?: string | null;
|
||||||
meta?: {
|
meta?: {
|
||||||
title?: string | null;
|
title?: string | null;
|
||||||
description?: string | null;
|
description?: string | null;
|
||||||
image?: (string | null) | Media;
|
image?: (number | null) | Media;
|
||||||
};
|
};
|
||||||
categories?:
|
categories?:
|
||||||
| {
|
| {
|
||||||
@@ -857,7 +1071,7 @@ export interface Search {
|
|||||||
* via the `definition` "payload-kv".
|
* via the `definition` "payload-kv".
|
||||||
*/
|
*/
|
||||||
export interface PayloadKv {
|
export interface PayloadKv {
|
||||||
id: string;
|
id: number;
|
||||||
key: string;
|
key: string;
|
||||||
data:
|
data:
|
||||||
| {
|
| {
|
||||||
@@ -874,7 +1088,7 @@ export interface PayloadKv {
|
|||||||
* via the `definition` "payload-jobs".
|
* via the `definition` "payload-jobs".
|
||||||
*/
|
*/
|
||||||
export interface PayloadJob {
|
export interface PayloadJob {
|
||||||
id: string;
|
id: number;
|
||||||
/**
|
/**
|
||||||
* Input data provided to the job
|
* Input data provided to the job
|
||||||
*/
|
*/
|
||||||
@@ -966,52 +1180,56 @@ export interface PayloadJob {
|
|||||||
* via the `definition` "payload-locked-documents".
|
* via the `definition` "payload-locked-documents".
|
||||||
*/
|
*/
|
||||||
export interface PayloadLockedDocument {
|
export interface PayloadLockedDocument {
|
||||||
id: string;
|
id: number;
|
||||||
document?:
|
document?:
|
||||||
| ({
|
| ({
|
||||||
relationTo: 'pages';
|
relationTo: 'pages';
|
||||||
value: string | Page;
|
value: number | Page;
|
||||||
} | null)
|
} | null)
|
||||||
| ({
|
| ({
|
||||||
relationTo: 'posts';
|
relationTo: 'posts';
|
||||||
value: string | Post;
|
value: number | Post;
|
||||||
|
} | null)
|
||||||
|
| ({
|
||||||
|
relationTo: 'projects';
|
||||||
|
value: number | Project;
|
||||||
} | null)
|
} | null)
|
||||||
| ({
|
| ({
|
||||||
relationTo: 'media';
|
relationTo: 'media';
|
||||||
value: string | Media;
|
value: number | Media;
|
||||||
} | null)
|
} | null)
|
||||||
| ({
|
| ({
|
||||||
relationTo: 'categories';
|
relationTo: 'categories';
|
||||||
value: string | Category;
|
value: number | Category;
|
||||||
} | null)
|
} | null)
|
||||||
| ({
|
| ({
|
||||||
relationTo: 'users';
|
relationTo: 'users';
|
||||||
value: string | User;
|
value: number | User;
|
||||||
} | null)
|
} | null)
|
||||||
| ({
|
| ({
|
||||||
relationTo: 'redirects';
|
relationTo: 'redirects';
|
||||||
value: string | Redirect;
|
value: number | Redirect;
|
||||||
} | null)
|
} | null)
|
||||||
| ({
|
| ({
|
||||||
relationTo: 'forms';
|
relationTo: 'forms';
|
||||||
value: string | Form;
|
value: number | Form;
|
||||||
} | null)
|
} | null)
|
||||||
| ({
|
| ({
|
||||||
relationTo: 'form-submissions';
|
relationTo: 'form-submissions';
|
||||||
value: string | FormSubmission;
|
value: number | FormSubmission;
|
||||||
} | null)
|
} | null)
|
||||||
| ({
|
| ({
|
||||||
relationTo: 'search';
|
relationTo: 'search';
|
||||||
value: string | Search;
|
value: number | Search;
|
||||||
} | null)
|
} | null)
|
||||||
| ({
|
| ({
|
||||||
relationTo: 'payload-folders';
|
relationTo: 'payload-folders';
|
||||||
value: string | FolderInterface;
|
value: number | FolderInterface;
|
||||||
} | null);
|
} | null);
|
||||||
globalSlug?: string | null;
|
globalSlug?: string | null;
|
||||||
user: {
|
user: {
|
||||||
relationTo: 'users';
|
relationTo: 'users';
|
||||||
value: string | User;
|
value: number | User;
|
||||||
};
|
};
|
||||||
updatedAt: string;
|
updatedAt: string;
|
||||||
createdAt: string;
|
createdAt: string;
|
||||||
@@ -1021,10 +1239,10 @@ export interface PayloadLockedDocument {
|
|||||||
* via the `definition` "payload-preferences".
|
* via the `definition` "payload-preferences".
|
||||||
*/
|
*/
|
||||||
export interface PayloadPreference {
|
export interface PayloadPreference {
|
||||||
id: string;
|
id: number;
|
||||||
user: {
|
user: {
|
||||||
relationTo: 'users';
|
relationTo: 'users';
|
||||||
value: string | User;
|
value: number | User;
|
||||||
};
|
};
|
||||||
key?: string | null;
|
key?: string | null;
|
||||||
value?:
|
value?:
|
||||||
@@ -1044,7 +1262,7 @@ export interface PayloadPreference {
|
|||||||
* via the `definition` "payload-migrations".
|
* via the `definition` "payload-migrations".
|
||||||
*/
|
*/
|
||||||
export interface PayloadMigration {
|
export interface PayloadMigration {
|
||||||
id: string;
|
id: number;
|
||||||
name?: string | null;
|
name?: string | null;
|
||||||
batch?: number | null;
|
batch?: number | null;
|
||||||
updatedAt: string;
|
updatedAt: string;
|
||||||
@@ -1216,6 +1434,67 @@ export interface PostsSelect<T extends boolean = true> {
|
|||||||
createdAt?: T;
|
createdAt?: T;
|
||||||
_status?: T;
|
_status?: T;
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* This interface was referenced by `Config`'s JSON-Schema
|
||||||
|
* via the `definition` "projects_select".
|
||||||
|
*/
|
||||||
|
export interface ProjectsSelect<T extends boolean = true> {
|
||||||
|
name?: T;
|
||||||
|
heroImage?: T;
|
||||||
|
shortDescription?: T;
|
||||||
|
description?: T;
|
||||||
|
problem?: T;
|
||||||
|
solution?: T;
|
||||||
|
results?: T;
|
||||||
|
client?: T;
|
||||||
|
year?: T;
|
||||||
|
duration?: T;
|
||||||
|
role?: T;
|
||||||
|
teamSize?: T;
|
||||||
|
techStack?:
|
||||||
|
| T
|
||||||
|
| {
|
||||||
|
name?: T;
|
||||||
|
category?: T;
|
||||||
|
id?: T;
|
||||||
|
};
|
||||||
|
highlights?:
|
||||||
|
| T
|
||||||
|
| {
|
||||||
|
title?: T;
|
||||||
|
description?: T;
|
||||||
|
id?: T;
|
||||||
|
};
|
||||||
|
gallery?:
|
||||||
|
| T
|
||||||
|
| {
|
||||||
|
image?: T;
|
||||||
|
caption?: T;
|
||||||
|
alt?: T;
|
||||||
|
id?: T;
|
||||||
|
};
|
||||||
|
liveUrl?: T;
|
||||||
|
githubUrl?: T;
|
||||||
|
videoUrl?: T;
|
||||||
|
caseStudyUrl?: T;
|
||||||
|
categories?: T;
|
||||||
|
relatedProjects?: T;
|
||||||
|
meta?:
|
||||||
|
| T
|
||||||
|
| {
|
||||||
|
title?: T;
|
||||||
|
image?: T;
|
||||||
|
description?: T;
|
||||||
|
};
|
||||||
|
featured?: T;
|
||||||
|
status?: T;
|
||||||
|
publishedAt?: T;
|
||||||
|
generateSlug?: T;
|
||||||
|
slug?: T;
|
||||||
|
updatedAt?: T;
|
||||||
|
createdAt?: T;
|
||||||
|
_status?: T;
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* This interface was referenced by `Config`'s JSON-Schema
|
* This interface was referenced by `Config`'s JSON-Schema
|
||||||
* via the `definition` "media_select".
|
* via the `definition` "media_select".
|
||||||
@@ -1316,8 +1595,13 @@ export interface MediaSelect<T extends boolean = true> {
|
|||||||
*/
|
*/
|
||||||
export interface CategoriesSelect<T extends boolean = true> {
|
export interface CategoriesSelect<T extends boolean = true> {
|
||||||
title?: T;
|
title?: T;
|
||||||
|
description?: T;
|
||||||
generateSlug?: T;
|
generateSlug?: T;
|
||||||
slug?: T;
|
slug?: T;
|
||||||
|
type?: T;
|
||||||
|
icon?: T;
|
||||||
|
color?: T;
|
||||||
|
order?: T;
|
||||||
parent?: T;
|
parent?: T;
|
||||||
breadcrumbs?:
|
breadcrumbs?:
|
||||||
| T
|
| T
|
||||||
@@ -1633,7 +1917,10 @@ export interface PayloadMigrationsSelect<T extends boolean = true> {
|
|||||||
* via the `definition` "header".
|
* via the `definition` "header".
|
||||||
*/
|
*/
|
||||||
export interface Header {
|
export interface Header {
|
||||||
id: string;
|
id: number;
|
||||||
|
/**
|
||||||
|
* Main navigation menu items
|
||||||
|
*/
|
||||||
navItems?:
|
navItems?:
|
||||||
| {
|
| {
|
||||||
link: {
|
link: {
|
||||||
@@ -1642,11 +1929,11 @@ export interface Header {
|
|||||||
reference?:
|
reference?:
|
||||||
| ({
|
| ({
|
||||||
relationTo: 'pages';
|
relationTo: 'pages';
|
||||||
value: string | Page;
|
value: number | Page;
|
||||||
} | null)
|
} | null)
|
||||||
| ({
|
| ({
|
||||||
relationTo: 'posts';
|
relationTo: 'posts';
|
||||||
value: string | Post;
|
value: number | Post;
|
||||||
} | null);
|
} | null);
|
||||||
url?: string | null;
|
url?: string | null;
|
||||||
label: string;
|
label: string;
|
||||||
@@ -1654,6 +1941,50 @@ export interface Header {
|
|||||||
id?: string | null;
|
id?: string | null;
|
||||||
}[]
|
}[]
|
||||||
| null;
|
| null;
|
||||||
|
ctaButton?: {
|
||||||
|
/**
|
||||||
|
* Show CTA button in header
|
||||||
|
*/
|
||||||
|
enabled?: boolean | null;
|
||||||
|
/**
|
||||||
|
* Button text (e.g., "Get in Touch", "Hire Me", "Let's Talk")
|
||||||
|
*/
|
||||||
|
text?: string | null;
|
||||||
|
/**
|
||||||
|
* Button URL or page slug
|
||||||
|
*/
|
||||||
|
link?: string | null;
|
||||||
|
/**
|
||||||
|
* Button visual style
|
||||||
|
*/
|
||||||
|
style?: ('primary' | 'secondary' | 'outline') | null;
|
||||||
|
};
|
||||||
|
announcementBar?: {
|
||||||
|
/**
|
||||||
|
* Show announcement bar above header
|
||||||
|
*/
|
||||||
|
enabled?: boolean | null;
|
||||||
|
/**
|
||||||
|
* Announcement text
|
||||||
|
*/
|
||||||
|
message?: string | null;
|
||||||
|
/**
|
||||||
|
* Optional link URL
|
||||||
|
*/
|
||||||
|
link?: string | null;
|
||||||
|
/**
|
||||||
|
* Link text (e.g., "Learn more")
|
||||||
|
*/
|
||||||
|
linkText?: string | null;
|
||||||
|
/**
|
||||||
|
* Bar style/color
|
||||||
|
*/
|
||||||
|
type?: ('info' | 'success' | 'warning' | 'promotion') | null;
|
||||||
|
/**
|
||||||
|
* Allow users to close the announcement
|
||||||
|
*/
|
||||||
|
dismissible?: boolean | null;
|
||||||
|
};
|
||||||
updatedAt?: string | null;
|
updatedAt?: string | null;
|
||||||
createdAt?: string | null;
|
createdAt?: string | null;
|
||||||
}
|
}
|
||||||
@@ -1662,7 +1993,10 @@ export interface Header {
|
|||||||
* via the `definition` "footer".
|
* via the `definition` "footer".
|
||||||
*/
|
*/
|
||||||
export interface Footer {
|
export interface Footer {
|
||||||
id: string;
|
id: number;
|
||||||
|
/**
|
||||||
|
* Main footer navigation links (Projects, Blog, About, Contact, Privacy, etc.)
|
||||||
|
*/
|
||||||
navItems?:
|
navItems?:
|
||||||
| {
|
| {
|
||||||
link: {
|
link: {
|
||||||
@@ -1671,11 +2005,11 @@ export interface Footer {
|
|||||||
reference?:
|
reference?:
|
||||||
| ({
|
| ({
|
||||||
relationTo: 'pages';
|
relationTo: 'pages';
|
||||||
value: string | Page;
|
value: number | Page;
|
||||||
} | null)
|
} | null)
|
||||||
| ({
|
| ({
|
||||||
relationTo: 'posts';
|
relationTo: 'posts';
|
||||||
value: string | Post;
|
value: number | Post;
|
||||||
} | null);
|
} | null);
|
||||||
url?: string | null;
|
url?: string | null;
|
||||||
label: string;
|
label: string;
|
||||||
@@ -1683,6 +2017,56 @@ export interface Footer {
|
|||||||
id?: string | null;
|
id?: string | null;
|
||||||
}[]
|
}[]
|
||||||
| null;
|
| null;
|
||||||
|
/**
|
||||||
|
* Social media links
|
||||||
|
*/
|
||||||
|
socialLinks?:
|
||||||
|
| {
|
||||||
|
platform: 'linkedin' | 'twitter' | 'github' | 'youtube' | 'facebook' | 'instagram';
|
||||||
|
url: string;
|
||||||
|
id?: string | null;
|
||||||
|
}[]
|
||||||
|
| null;
|
||||||
|
/**
|
||||||
|
* MANDATORY for Kit Digital grant: EU, Next Generation EU, Plan de Recuperación logos
|
||||||
|
*/
|
||||||
|
euFundingLogos: {
|
||||||
|
logo: number | Media;
|
||||||
|
/**
|
||||||
|
* Alt text (e.g., "Financiado por la Unión Europea - NextGenerationEU")
|
||||||
|
*/
|
||||||
|
alt: string;
|
||||||
|
/**
|
||||||
|
* Optional link URL
|
||||||
|
*/
|
||||||
|
link?: string | null;
|
||||||
|
id?: string | null;
|
||||||
|
}[];
|
||||||
|
personalInfo?: {
|
||||||
|
name?: string | null;
|
||||||
|
/**
|
||||||
|
* Professional title or tagline (e.g., "Full Stack Developer", "Tech Consultant")
|
||||||
|
*/
|
||||||
|
professionalTitle?: string | null;
|
||||||
|
/**
|
||||||
|
* Location (e.g., "Barcelona, Spain")
|
||||||
|
*/
|
||||||
|
location?: string | null;
|
||||||
|
email?: string | null;
|
||||||
|
phone?: string | null;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* Enable newsletter signup in footer
|
||||||
|
*/
|
||||||
|
newsletterEnabled?: boolean | null;
|
||||||
|
/**
|
||||||
|
* Newsletter section heading
|
||||||
|
*/
|
||||||
|
newsletterHeading?: string | null;
|
||||||
|
/**
|
||||||
|
* Newsletter description text
|
||||||
|
*/
|
||||||
|
newsletterDescription?: string | null;
|
||||||
updatedAt?: string | null;
|
updatedAt?: string | null;
|
||||||
createdAt?: string | null;
|
createdAt?: string | null;
|
||||||
}
|
}
|
||||||
@@ -1705,6 +2089,24 @@ export interface HeaderSelect<T extends boolean = true> {
|
|||||||
};
|
};
|
||||||
id?: T;
|
id?: T;
|
||||||
};
|
};
|
||||||
|
ctaButton?:
|
||||||
|
| T
|
||||||
|
| {
|
||||||
|
enabled?: T;
|
||||||
|
text?: T;
|
||||||
|
link?: T;
|
||||||
|
style?: T;
|
||||||
|
};
|
||||||
|
announcementBar?:
|
||||||
|
| T
|
||||||
|
| {
|
||||||
|
enabled?: T;
|
||||||
|
message?: T;
|
||||||
|
link?: T;
|
||||||
|
linkText?: T;
|
||||||
|
type?: T;
|
||||||
|
dismissible?: T;
|
||||||
|
};
|
||||||
updatedAt?: T;
|
updatedAt?: T;
|
||||||
createdAt?: T;
|
createdAt?: T;
|
||||||
globalType?: T;
|
globalType?: T;
|
||||||
@@ -1728,6 +2130,33 @@ export interface FooterSelect<T extends boolean = true> {
|
|||||||
};
|
};
|
||||||
id?: T;
|
id?: T;
|
||||||
};
|
};
|
||||||
|
socialLinks?:
|
||||||
|
| T
|
||||||
|
| {
|
||||||
|
platform?: T;
|
||||||
|
url?: T;
|
||||||
|
id?: T;
|
||||||
|
};
|
||||||
|
euFundingLogos?:
|
||||||
|
| T
|
||||||
|
| {
|
||||||
|
logo?: T;
|
||||||
|
alt?: T;
|
||||||
|
link?: T;
|
||||||
|
id?: T;
|
||||||
|
};
|
||||||
|
personalInfo?:
|
||||||
|
| T
|
||||||
|
| {
|
||||||
|
name?: T;
|
||||||
|
professionalTitle?: T;
|
||||||
|
location?: T;
|
||||||
|
email?: T;
|
||||||
|
phone?: T;
|
||||||
|
};
|
||||||
|
newsletterEnabled?: T;
|
||||||
|
newsletterHeading?: T;
|
||||||
|
newsletterDescription?: T;
|
||||||
updatedAt?: T;
|
updatedAt?: T;
|
||||||
createdAt?: T;
|
createdAt?: T;
|
||||||
globalType?: T;
|
globalType?: T;
|
||||||
@@ -1743,14 +2172,18 @@ export interface TaskSchedulePublish {
|
|||||||
doc?:
|
doc?:
|
||||||
| ({
|
| ({
|
||||||
relationTo: 'pages';
|
relationTo: 'pages';
|
||||||
value: string | Page;
|
value: number | Page;
|
||||||
} | null)
|
} | null)
|
||||||
| ({
|
| ({
|
||||||
relationTo: 'posts';
|
relationTo: 'posts';
|
||||||
value: string | Post;
|
value: number | Post;
|
||||||
|
} | null)
|
||||||
|
| ({
|
||||||
|
relationTo: 'projects';
|
||||||
|
value: number | Project;
|
||||||
} | null);
|
} | null);
|
||||||
global?: string | null;
|
global?: string | null;
|
||||||
user?: (string | null) | User;
|
user?: (number | null) | User;
|
||||||
};
|
};
|
||||||
output?: unknown;
|
output?: unknown;
|
||||||
}
|
}
|
||||||
|
|||||||
+20
-1
@@ -10,6 +10,7 @@ import { Categories } from './collections/Categories'
|
|||||||
import { Media } from './collections/Media'
|
import { Media } from './collections/Media'
|
||||||
import { Pages } from './collections/Pages'
|
import { Pages } from './collections/Pages'
|
||||||
import { Posts } from './collections/Posts'
|
import { Posts } from './collections/Posts'
|
||||||
|
import { Projects } from './collections/Projects'
|
||||||
import { Users } from './collections/Users'
|
import { Users } from './collections/Users'
|
||||||
import { Footer } from './Footer/config'
|
import { Footer } from './Footer/config'
|
||||||
import { Header } from './Header/config'
|
import { Header } from './Header/config'
|
||||||
@@ -57,6 +58,24 @@ export default buildConfig({
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
localization: {
|
||||||
|
locales: [
|
||||||
|
{
|
||||||
|
label: 'English',
|
||||||
|
code: 'en',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Español',
|
||||||
|
code: 'es',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Català',
|
||||||
|
code: 'ca',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
defaultLocale: 'en',
|
||||||
|
fallback: true,
|
||||||
|
},
|
||||||
// This config helps us configure global or default features that the other editors can inherit
|
// This config helps us configure global or default features that the other editors can inherit
|
||||||
editor: defaultLexical,
|
editor: defaultLexical,
|
||||||
db: postgresAdapter({
|
db: postgresAdapter({
|
||||||
@@ -64,7 +83,7 @@ export default buildConfig({
|
|||||||
connectionString: process.env.DATABASE_URI || '',
|
connectionString: process.env.DATABASE_URI || '',
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
collections: [Pages, Posts, Media, Categories, Users],
|
collections: [Pages, Posts, Projects, Media, Categories, Users],
|
||||||
cors: [getServerSideURL()].filter(Boolean),
|
cors: [getServerSideURL()].filter(Boolean),
|
||||||
globals: [Header, Footer],
|
globals: [Header, Footer],
|
||||||
plugins: [
|
plugins: [
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ const generateURL: GenerateURL<Post | Page> = ({ doc }) => {
|
|||||||
|
|
||||||
export const plugins: Plugin[] = [
|
export const plugins: Plugin[] = [
|
||||||
redirectsPlugin({
|
redirectsPlugin({
|
||||||
collections: ['pages', 'posts'],
|
collections: ['pages', 'posts', 'projects'],
|
||||||
overrides: {
|
overrides: {
|
||||||
// @ts-expect-error - This is a valid override, mapped fields don't resolve to the same type
|
// @ts-expect-error - This is a valid override, mapped fields don't resolve to the same type
|
||||||
fields: ({ defaultFields }) => {
|
fields: ({ defaultFields }) => {
|
||||||
@@ -81,7 +81,7 @@ export const plugins: Plugin[] = [
|
|||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
searchPlugin({
|
searchPlugin({
|
||||||
collections: ['posts'],
|
collections: ['posts', 'projects'],
|
||||||
beforeSync: beforeSyncWithSearch,
|
beforeSync: beforeSyncWithSearch,
|
||||||
searchOverrides: {
|
searchOverrides: {
|
||||||
fields: ({ defaultFields }) => {
|
fields: ({ defaultFields }) => {
|
||||||
|
|||||||
Reference in New Issue
Block a user