Includes all Phase 1 features: - Search-first navigation with auto-complete - Responsive reading interface (desktop/tablet/mobile) - 4 customization presets + full fine-tuning controls - Layered details panel with notes, bookmarks, highlights - Smart offline caching with IndexedDB and auto-sync - Full accessibility (WCAG 2.1 AA) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
103 lines
2.4 KiB
TypeScript
103 lines
2.4 KiB
TypeScript
import { CollectionConfig } from 'payload';
|
|
|
|
export const Products: CollectionConfig = {
|
|
slug: 'products',
|
|
admin: {
|
|
useAsTitle: 'name',
|
|
defaultColumns: ['name', 'stripeProductId', 'active', 'createdAt'],
|
|
group: 'E-Commerce',
|
|
},
|
|
fields: [
|
|
{
|
|
name: 'name',
|
|
type: 'text',
|
|
required: true,
|
|
localized: true,
|
|
},
|
|
{
|
|
name: 'description',
|
|
type: 'richText',
|
|
localized: true,
|
|
},
|
|
{
|
|
name: 'stripeProductId',
|
|
type: 'text',
|
|
unique: true,
|
|
index: true,
|
|
admin: {
|
|
readOnly: true,
|
|
},
|
|
},
|
|
{
|
|
name: 'active',
|
|
type: 'checkbox',
|
|
defaultValue: true,
|
|
required: true,
|
|
},
|
|
{
|
|
name: 'metadata',
|
|
type: 'group',
|
|
fields: [
|
|
{
|
|
name: 'planType',
|
|
type: 'select',
|
|
options: [
|
|
{ label: 'Free', value: 'free' },
|
|
{ label: 'Premium', value: 'premium' },
|
|
{ label: 'Enterprise', value: 'enterprise' },
|
|
],
|
|
required: true,
|
|
},
|
|
{
|
|
name: 'features',
|
|
type: 'array',
|
|
fields: [
|
|
{
|
|
name: 'feature',
|
|
type: 'text',
|
|
required: true,
|
|
},
|
|
{
|
|
name: 'included',
|
|
type: 'checkbox',
|
|
defaultValue: true,
|
|
},
|
|
{
|
|
name: 'limit',
|
|
type: 'number',
|
|
admin: {
|
|
condition: (data, siblingData) => !siblingData.included,
|
|
},
|
|
},
|
|
],
|
|
},
|
|
],
|
|
},
|
|
{
|
|
name: 'prices',
|
|
type: 'relationship',
|
|
relationTo: 'prices',
|
|
hasMany: true,
|
|
admin: {
|
|
description: 'Associated price points for this product',
|
|
},
|
|
},
|
|
],
|
|
hooks: {
|
|
beforeChange: [
|
|
async ({ data, operation }) => {
|
|
if (operation === 'create' && !data.stripeProductId) {
|
|
console.log('Product created:', data.name, '- Stripe ID should be synced from Stripe plugin');
|
|
}
|
|
return data;
|
|
},
|
|
],
|
|
},
|
|
access: {
|
|
read: () => true, // Products are public
|
|
create: ({ req }) => req.user?.role === 'admin' || req.user?.role === 'super-admin',
|
|
update: ({ req }) => req.user?.role === 'admin' || req.user?.role === 'super-admin',
|
|
delete: ({ req }) => req.user?.role === 'super-admin',
|
|
},
|
|
};
|