Add Portfolio collection with 7 fields
Create Portfolio collection for managing website portfolio items. Includes title, slug, url, image, description, websiteType, and tags fields. Configured access control for authenticated create/ update/delete operations with public read access.
This commit is contained in:
@@ -0,0 +1,60 @@
|
||||
import { Portfolio } from '../index'
|
||||
|
||||
describe('Portfolio Collection', () => {
|
||||
it('should have correct slug', () => {
|
||||
expect(Portfolio.slug).toBe('portfolio')
|
||||
})
|
||||
|
||||
it('should have all required fields', () => {
|
||||
const fieldNames = Portfolio.fields.map((f) => ('name' in f ? f.name : null))
|
||||
expect(fieldNames).toContain('title')
|
||||
expect(fieldNames).toContain('slug')
|
||||
expect(fieldNames).toContain('url')
|
||||
expect(fieldNames).toContain('image')
|
||||
expect(fieldNames).toContain('description')
|
||||
expect(fieldNames).toContain('websiteType')
|
||||
expect(fieldNames).toContain('tags')
|
||||
})
|
||||
|
||||
it('should have correct access control', () => {
|
||||
expect(Portfolio.access.read).toBeDefined()
|
||||
expect(Portfolio.access.create).toBeDefined()
|
||||
expect(Portfolio.access.update).toBeDefined()
|
||||
expect(Portfolio.access.delete).toBeDefined()
|
||||
})
|
||||
|
||||
it('should have admin configuration', () => {
|
||||
expect(Portfolio.admin).toBeDefined()
|
||||
expect(Portfolio.admin?.useAsTitle).toBe('title')
|
||||
expect(Portfolio.admin?.defaultColumns).toEqual(['title', 'websiteType', 'updatedAt'])
|
||||
})
|
||||
|
||||
it('should have websiteType field with correct options', () => {
|
||||
const websiteTypeField = Portfolio.fields.find((f) => f.name === 'websiteType')
|
||||
expect(websiteTypeField).toBeDefined()
|
||||
expect(websiteTypeField?.type).toBe('select')
|
||||
if (websiteTypeField?.type === 'select') {
|
||||
const optionValues = websiteTypeField.options.map((o) => o.value)
|
||||
expect(optionValues).toContain('corporate')
|
||||
expect(optionValues).toContain('ecommerce')
|
||||
expect(optionValues).toContain('landing')
|
||||
expect(optionValues).toContain('brand')
|
||||
expect(optionValues).toContain('other')
|
||||
}
|
||||
})
|
||||
|
||||
it('should have tags field as array', () => {
|
||||
const tagsField = Portfolio.fields.find((f) => f.name === 'tags')
|
||||
expect(tagsField).toBeDefined()
|
||||
expect(tagsField?.type).toBe('array')
|
||||
})
|
||||
|
||||
it('should have image field with relation to media', () => {
|
||||
const imageField = Portfolio.fields.find((f) => f.name === 'image')
|
||||
expect(imageField).toBeDefined()
|
||||
expect(imageField?.type).toBe('upload')
|
||||
if (imageField?.type === 'upload') {
|
||||
expect(imageField.relationTo).toBe('media')
|
||||
}
|
||||
})
|
||||
})
|
||||
75
apps/backend/src/collections/Portfolio/index.ts
Normal file
75
apps/backend/src/collections/Portfolio/index.ts
Normal file
@@ -0,0 +1,75 @@
|
||||
import type { CollectionConfig } from 'payload'
|
||||
|
||||
import { authenticated } from '../../access/authenticated'
|
||||
import { anyone } from '../../access/anyone'
|
||||
import { slugField } from '@/fields/slug'
|
||||
|
||||
export const Portfolio: CollectionConfig = {
|
||||
slug: 'portfolio',
|
||||
access: {
|
||||
create: authenticated,
|
||||
read: anyone,
|
||||
update: authenticated,
|
||||
delete: authenticated,
|
||||
},
|
||||
admin: {
|
||||
useAsTitle: 'title',
|
||||
defaultColumns: ['title', 'websiteType', 'updatedAt'],
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
name: 'title',
|
||||
type: 'text',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
name: 'url',
|
||||
type: 'text',
|
||||
admin: {
|
||||
description: 'Website URL (e.g., https://example.com)',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'image',
|
||||
type: 'upload',
|
||||
relationTo: 'media',
|
||||
required: true,
|
||||
admin: {
|
||||
description: 'Preview image stored in R2',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'description',
|
||||
type: 'textarea',
|
||||
},
|
||||
{
|
||||
name: 'websiteType',
|
||||
type: 'select',
|
||||
options: [
|
||||
{ label: '企業官網', value: 'corporate' },
|
||||
{ label: '電商網站', value: 'ecommerce' },
|
||||
{ label: '活動頁面', value: 'landing' },
|
||||
{ label: '品牌網站', value: 'brand' },
|
||||
{ label: '其他', value: 'other' },
|
||||
],
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
name: 'tags',
|
||||
type: 'array',
|
||||
fields: [
|
||||
{
|
||||
name: 'tag',
|
||||
type: 'text',
|
||||
},
|
||||
],
|
||||
},
|
||||
...slugField(),
|
||||
],
|
||||
versions: {
|
||||
drafts: {
|
||||
autosave: true,
|
||||
},
|
||||
maxPerDoc: 10,
|
||||
},
|
||||
}
|
||||
Reference in New Issue
Block a user