diff --git a/src/collections/Projects.ts b/src/collections/Projects.ts index 922adc3..84f0825 100644 --- a/src/collections/Projects.ts +++ b/src/collections/Projects.ts @@ -3,12 +3,13 @@ import type { CollectionConfig } from 'payload' export const Projects: CollectionConfig = { slug: 'projects', admin: { - useAsTitle: 'name', - defaultColumns: ['name', 'technologies', 'featured', 'order'], + useAsTitle: 'title', + defaultColumns: ['title', 'featured', 'order', 'createdAt'], + listSearchableFields: ['title', 'excerpt'], }, fields: [ { - name: 'name', + name: 'title', type: 'text', required: true, }, @@ -18,11 +19,20 @@ export const Projects: CollectionConfig = { unique: true, index: true, }, + { + name: 'excerpt', + type: 'text', + required: true, + admin: { + description: 'One-liner for project cards (keep it punchy)', + }, + }, { name: 'description', - type: 'textarea', + type: 'richText', + required: true, admin: { - description: 'Brief description - 1-2 sentences for card views', + description: 'Problem, solution, outcome. What did you build and why does it matter?', }, }, { @@ -36,8 +46,39 @@ export const Projects: CollectionConfig = { name: 'featuredImage', type: 'upload', relationTo: 'media', + required: true, + }, + { + name: 'gallery', + type: 'array', + fields: [ + { + name: 'image', + type: 'upload', + relationTo: 'media', + required: true, + }, + { + name: 'caption', + type: 'text', + }, + ], admin: { - description: 'Thumbnail image for project cards', + description: 'Additional screenshots/images for the project detail page', + }, + }, + { + name: 'liveUrl', + type: 'text', + admin: { + description: 'URL to the live project/demo', + }, + }, + { + name: 'sourceUrl', + type: 'text', + admin: { + description: 'URL to source code (GitHub, GitLab, etc.)', }, }, { @@ -45,21 +86,36 @@ export const Projects: CollectionConfig = { type: 'relationship', relationTo: 'technologies', hasMany: true, + required: true, }, { - name: 'demoUrl', - type: 'text', + name: 'context', + type: 'select', + options: [ + { label: 'Personal Project', value: 'personal' }, + { label: 'Work Project', value: 'work' }, + { label: 'Learning Exercise', value: 'learning' }, + { label: 'Open Source Contribution', value: 'opensource' }, + ], + admin: { + description: + 'Tells recruiters where this project came from without them navigating to Experience', + }, }, { - name: 'repoUrl', + name: 'company', type: 'text', + admin: { + description: 'If work project, which company? (shown alongside context)', + condition: (data) => data?.context === 'work', + }, }, { name: 'featured', type: 'checkbox', defaultValue: false, admin: { - description: 'Enable to feature on homepage', + description: 'Featured projects appear on the homepage and get priority placement', }, }, { diff --git a/src/payload-types.ts b/src/payload-types.ts index 9a37ddf..d3083a2 100644 --- a/src/payload-types.ts +++ b/src/payload-types.ts @@ -59,501 +59,552 @@ export type SupportedTimezones = | 'Pacific/Guam' | 'Pacific/Noumea' | 'Pacific/Auckland' - | 'Pacific/Fiji'; + | 'Pacific/Fiji' export interface Config { auth: { - users: UserAuthOperations; - }; - blocks: {}; + users: UserAuthOperations + } + blocks: {} collections: { - users: User; - media: Media; - education: Education; - technologies: Technology; - projects: Project; - 'payload-kv': PayloadKv; - 'payload-locked-documents': PayloadLockedDocument; - 'payload-preferences': PayloadPreference; - 'payload-migrations': PayloadMigration; - }; - collectionsJoins: {}; + users: User + media: Media + education: Education + technologies: Technology + projects: Project + 'payload-kv': PayloadKv + 'payload-locked-documents': PayloadLockedDocument + 'payload-preferences': PayloadPreference + 'payload-migrations': PayloadMigration + } + collectionsJoins: {} collectionsSelect: { - users: UsersSelect | UsersSelect; - media: MediaSelect | MediaSelect; - education: EducationSelect | EducationSelect; - technologies: TechnologiesSelect | TechnologiesSelect; - projects: ProjectsSelect | ProjectsSelect; - 'payload-kv': PayloadKvSelect | PayloadKvSelect; - 'payload-locked-documents': PayloadLockedDocumentsSelect | PayloadLockedDocumentsSelect; - 'payload-preferences': PayloadPreferencesSelect | PayloadPreferencesSelect; - 'payload-migrations': PayloadMigrationsSelect | PayloadMigrationsSelect; - }; + users: UsersSelect | UsersSelect + media: MediaSelect | MediaSelect + education: EducationSelect | EducationSelect + technologies: TechnologiesSelect | TechnologiesSelect + projects: ProjectsSelect | ProjectsSelect + 'payload-kv': PayloadKvSelect | PayloadKvSelect + 'payload-locked-documents': + | PayloadLockedDocumentsSelect + | PayloadLockedDocumentsSelect + 'payload-preferences': PayloadPreferencesSelect | PayloadPreferencesSelect + 'payload-migrations': PayloadMigrationsSelect | PayloadMigrationsSelect + } db: { - defaultIDType: number; - }; - fallbackLocale: null; + defaultIDType: number + } + fallbackLocale: null globals: { - homepage: Homepage; - contact: Contact; - }; + homepage: Homepage + contact: Contact + } globalsSelect: { - homepage: HomepageSelect | HomepageSelect; - contact: ContactSelect | ContactSelect; - }; - locale: null; + homepage: HomepageSelect | HomepageSelect + contact: ContactSelect | ContactSelect + } + locale: null widgets: { - collections: CollectionsWidget; - }; - user: User; + collections: CollectionsWidget + } + user: User jobs: { - tasks: unknown; - workflows: unknown; - }; + tasks: unknown + workflows: unknown + } } export interface UserAuthOperations { forgotPassword: { - email: string; - password: string; - }; + email: string + password: string + } login: { - email: string; - password: string; - }; + email: string + password: string + } registerFirstUser: { - email: string; - password: string; - }; + email: string + password: string + } unlock: { - email: string; - password: string; - }; + email: string + password: string + } } /** * This interface was referenced by `Config`'s JSON-Schema * via the `definition` "users". */ export interface User { - id: number; - updatedAt: string; - createdAt: string; - email: string; - resetPasswordToken?: string | null; - resetPasswordExpiration?: string | null; - salt?: string | null; - hash?: string | null; - loginAttempts?: number | null; - lockUntil?: string | null; + id: number + updatedAt: string + createdAt: string + email: string + resetPasswordToken?: string | null + resetPasswordExpiration?: string | null + salt?: string | null + hash?: string | null + loginAttempts?: number | null + lockUntil?: string | null sessions?: | { - id: string; - createdAt?: string | null; - expiresAt: string; + id: string + createdAt?: string | null + expiresAt: string }[] - | null; - password?: string | null; - collection: 'users'; + | null + password?: string | null + collection: 'users' } /** * This interface was referenced by `Config`'s JSON-Schema * via the `definition` "media". */ export interface Media { - id: number; - alt: string; - updatedAt: string; - createdAt: string; - url?: string | null; - thumbnailURL?: string | null; - filename?: string | null; - mimeType?: string | null; - filesize?: number | null; - width?: number | null; - height?: number | null; - focalX?: number | null; - focalY?: number | null; + id: number + alt: string + updatedAt: string + createdAt: string + url?: string | null + thumbnailURL?: string | null + filename?: string | null + mimeType?: string | null + filesize?: number | null + width?: number | null + height?: number | null + focalX?: number | null + focalY?: number | null } /** * This interface was referenced by `Config`'s JSON-Schema * via the `definition` "education". */ export interface Education { - id: number; - institution: string; + id: number + institution: string /** * e.g., "Bachelor of Science", "Master of Arts" */ - degree: string; + degree: string /** * e.g., "Computer Science", "Software Engineering" */ - fieldOfStudy: string; - startDate: string; - endDate?: string | null; + fieldOfStudy: string + startDate: string + endDate?: string | null /** * Optional: notable achievements, relevant coursework, thesis, etc. */ description?: { root: { - type: string; + 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; - updatedAt: string; - createdAt: string; + 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 + updatedAt: string + createdAt: string } /** * This interface was referenced by `Config`'s JSON-Schema * via the `definition` "technologies". */ export interface Technology { - id: number; - name: string; - slug?: string | null; - icon?: (number | null) | Media; - category?: ('frontend' | 'backend' | 'database' | 'devops' | 'tools' | 'languages') | null; + id: number + name: string + slug?: string | null + icon?: (number | null) | Media + category?: ('frontend' | 'backend' | 'database' | 'devops' | 'tools' | 'languages') | null /** * Display order (lower numbers appear first) */ - order?: number | null; - updatedAt: string; - createdAt: string; + order?: number | null + updatedAt: string + createdAt: string } /** * This interface was referenced by `Config`'s JSON-Schema * via the `definition` "projects". */ export interface Project { - id: number; - name: string; - slug?: string | null; + id: number + title: string + slug?: string | null /** - * Brief description - 1-2 sentences for card views + * One-liner for project cards (keep it punchy) */ - description?: string | null; + excerpt: string + /** + * Problem, solution, outcome. What did you build and why does it matter? + */ + 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 + } /** * Full project description for project detail pages */ content?: { root: { - type: string; + 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; + 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 + featuredImage: number | Media /** - * Thumbnail image for project cards + * Additional screenshots/images for the project detail page */ - featuredImage?: (number | null) | Media; - technologies?: (number | Technology)[] | null; - demoUrl?: string | null; - repoUrl?: string | null; + gallery?: + | { + image: number | Media + caption?: string | null + id?: string | null + }[] + | null + /** + * URL to the live project/demo + */ + liveUrl?: string | null /** - * Enable to feature on homepage + * URL to source code (GitHub, GitLab, etc.) */ - featured?: boolean | null; + sourceUrl?: string | null + technologies: (number | Technology)[] + /** + * Tells recruiters where this project came from without them navigating to Experience + */ + context?: ('personal' | 'work' | 'learning' | 'opensource') | null + /** + * If work project, which company? (shown alongside context) + */ + company?: string | null + /** + * Featured projects appear on the homepage and get priority placement + */ + featured?: boolean | null /** * Display order (lower numbers appear first) */ - order?: number | null; - updatedAt: string; - createdAt: string; + order?: number | null + updatedAt: string + createdAt: string } /** * This interface was referenced by `Config`'s JSON-Schema * via the `definition` "payload-kv". */ export interface PayloadKv { - id: number; - key: string; + id: number + key: string data: | { - [k: string]: unknown; + [k: string]: unknown } | unknown[] | string | number | boolean - | null; + | null } /** * This interface was referenced by `Config`'s JSON-Schema * via the `definition` "payload-locked-documents". */ export interface PayloadLockedDocument { - id: number; + id: number document?: | ({ - relationTo: 'users'; - value: number | User; + relationTo: 'users' + value: number | User } | null) | ({ - relationTo: 'media'; - value: number | Media; + relationTo: 'media' + value: number | Media } | null) | ({ - relationTo: 'education'; - value: number | Education; + relationTo: 'education' + value: number | Education } | null) | ({ - relationTo: 'technologies'; - value: number | Technology; + relationTo: 'technologies' + value: number | Technology } | null) | ({ - relationTo: 'projects'; - value: number | Project; - } | null); - globalSlug?: string | null; + relationTo: 'projects' + value: number | Project + } | null) + globalSlug?: string | null user: { - relationTo: 'users'; - value: number | User; - }; - updatedAt: string; - createdAt: string; + relationTo: 'users' + value: number | User + } + updatedAt: string + createdAt: string } /** * This interface was referenced by `Config`'s JSON-Schema * via the `definition` "payload-preferences". */ export interface PayloadPreference { - id: number; + id: number user: { - relationTo: 'users'; - value: number | User; - }; - key?: string | null; + relationTo: 'users' + value: number | User + } + key?: string | null value?: | { - [k: string]: unknown; + [k: string]: unknown } | unknown[] | string | number | boolean - | null; - updatedAt: string; - createdAt: string; + | null + updatedAt: string + createdAt: string } /** * This interface was referenced by `Config`'s JSON-Schema * via the `definition` "payload-migrations". */ export interface PayloadMigration { - id: number; - name?: string | null; - batch?: number | null; - updatedAt: string; - createdAt: string; + id: number + name?: string | null + batch?: number | null + updatedAt: string + createdAt: string } /** * This interface was referenced by `Config`'s JSON-Schema * via the `definition` "users_select". */ export interface UsersSelect { - updatedAt?: T; - createdAt?: T; - email?: T; - resetPasswordToken?: T; - resetPasswordExpiration?: T; - salt?: T; - hash?: T; - loginAttempts?: T; - lockUntil?: T; + updatedAt?: T + createdAt?: T + email?: T + resetPasswordToken?: T + resetPasswordExpiration?: T + salt?: T + hash?: T + loginAttempts?: T + lockUntil?: T sessions?: | T | { - id?: T; - createdAt?: T; - expiresAt?: T; - }; + id?: T + createdAt?: T + expiresAt?: T + } } /** * This interface was referenced by `Config`'s JSON-Schema * via the `definition` "media_select". */ export interface MediaSelect { - alt?: T; - updatedAt?: T; - createdAt?: T; - url?: T; - thumbnailURL?: T; - filename?: T; - mimeType?: T; - filesize?: T; - width?: T; - height?: T; - focalX?: T; - focalY?: T; + alt?: T + updatedAt?: T + createdAt?: T + url?: T + thumbnailURL?: T + filename?: T + mimeType?: T + filesize?: T + width?: T + height?: T + focalX?: T + focalY?: T } /** * This interface was referenced by `Config`'s JSON-Schema * via the `definition` "education_select". */ export interface EducationSelect { - institution?: T; - degree?: T; - fieldOfStudy?: T; - startDate?: T; - endDate?: T; - description?: T; - updatedAt?: T; - createdAt?: T; + institution?: T + degree?: T + fieldOfStudy?: T + startDate?: T + endDate?: T + description?: T + updatedAt?: T + createdAt?: T } /** * This interface was referenced by `Config`'s JSON-Schema * via the `definition` "technologies_select". */ export interface TechnologiesSelect { - name?: T; - slug?: T; - icon?: T; - category?: T; - order?: T; - updatedAt?: T; - createdAt?: T; + name?: T + slug?: T + icon?: T + category?: T + order?: T + updatedAt?: T + createdAt?: T } /** * This interface was referenced by `Config`'s JSON-Schema * via the `definition` "projects_select". */ export interface ProjectsSelect { - name?: T; - slug?: T; - description?: T; - content?: T; - featuredImage?: T; - technologies?: T; - demoUrl?: T; - repoUrl?: T; - featured?: T; - order?: T; - updatedAt?: T; - createdAt?: T; + title?: T + slug?: T + excerpt?: T + description?: T + content?: T + featuredImage?: T + gallery?: + | T + | { + image?: T + caption?: T + id?: T + } + liveUrl?: T + sourceUrl?: T + technologies?: T + context?: T + company?: T + featured?: T + order?: T + updatedAt?: T + createdAt?: T } /** * This interface was referenced by `Config`'s JSON-Schema * via the `definition` "payload-kv_select". */ export interface PayloadKvSelect { - key?: T; - data?: T; + key?: T + data?: T } /** * This interface was referenced by `Config`'s JSON-Schema * via the `definition` "payload-locked-documents_select". */ export interface PayloadLockedDocumentsSelect { - document?: T; - globalSlug?: T; - user?: T; - updatedAt?: T; - createdAt?: T; + document?: T + globalSlug?: T + user?: T + updatedAt?: T + createdAt?: T } /** * This interface was referenced by `Config`'s JSON-Schema * via the `definition` "payload-preferences_select". */ export interface PayloadPreferencesSelect { - user?: T; - key?: T; - value?: T; - updatedAt?: T; - createdAt?: T; + user?: T + key?: T + value?: T + updatedAt?: T + createdAt?: T } /** * This interface was referenced by `Config`'s JSON-Schema * via the `definition` "payload-migrations_select". */ export interface PayloadMigrationsSelect { - name?: T; - batch?: T; - updatedAt?: T; - createdAt?: T; + name?: T + batch?: T + updatedAt?: T + createdAt?: T } /** * This interface was referenced by `Config`'s JSON-Schema * via the `definition` "homepage". */ export interface Homepage { - id: number; + id: number hero: { /** * Main headline (e.g., "Hi, I'm Hugo") */ - title: string; + title: string /** * Tagline (e.g., "Full-stack developer building things that matter") */ - subtitle: string; + subtitle: string /** * Brief intro paragraph. Keep it short - recruiters scan, they don't read. */ intro?: { root: { - type: string; + 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; - }; + 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 + } /** * Select 3 of your best projects to feature on the homepage */ - featuredProjects?: (number | Project)[] | null; + featuredProjects?: (number | Project)[] | null /** * Resume/CV PDF - this is a primary CTA for recruiters */ - resume: number | Media; - updatedAt?: string | null; - createdAt?: string | null; + resume: number | Media + updatedAt?: string | null + createdAt?: string | null } /** * This interface was referenced by `Config`'s JSON-Schema * via the `definition` "contact". */ export interface Contact { - id: number; + id: number /** * Primary contact email */ - email: string; + email: string /** * Social/professional links (GitHub, LinkedIn, etc.) */ socialLinks?: | { - platform: 'github' | 'linkedin' | 'twitter' | 'mastodon' | 'website' | 'other'; - url: string; - id?: string | null; + platform: 'github' | 'linkedin' | 'twitter' | 'mastodon' | 'website' | 'other' + url: string + id?: string | null }[] - | null; - updatedAt?: string | null; - createdAt?: string | null; + | null + updatedAt?: string | null + createdAt?: string | null } /** * This interface was referenced by `Config`'s JSON-Schema @@ -563,52 +614,52 @@ export interface HomepageSelect { hero?: | T | { - title?: T; - subtitle?: T; - intro?: T; - }; - featuredProjects?: T; - resume?: T; - updatedAt?: T; - createdAt?: T; - globalType?: T; + title?: T + subtitle?: T + intro?: T + } + featuredProjects?: T + resume?: T + updatedAt?: T + createdAt?: T + globalType?: T } /** * This interface was referenced by `Config`'s JSON-Schema * via the `definition` "contact_select". */ export interface ContactSelect { - email?: T; + email?: T socialLinks?: | T | { - platform?: T; - url?: T; - id?: T; - }; - updatedAt?: T; - createdAt?: T; - globalType?: T; + platform?: T + url?: T + id?: T + } + updatedAt?: T + createdAt?: T + globalType?: T } /** * This interface was referenced by `Config`'s JSON-Schema +>>>>>>> origin/main * via the `definition` "collections_widget". */ export interface CollectionsWidget { data?: { - [k: string]: unknown; - }; - width: 'full'; + [k: string]: unknown + } + width: 'full' } /** * This interface was referenced by `Config`'s JSON-Schema * via the `definition` "auth". */ export interface Auth { - [k: string]: unknown; + [k: string]: unknown } - declare module 'payload' { export interface GeneratedTypes extends Config {} -} \ No newline at end of file +}