import type { CollectionConfig } from 'payload' import { isSuperAdmin } from '@/access/isSuperAdmin' import { hasTenantRole } from '@/access/roles' import { setCreatedBy } from './hooks/setCreatedBy' import { generateTitle } from './hooks/generateTitle' import { kitchenReportEndpoint } from './endpoints/kitchenReport' /** * Meal Orders Collection * * Represents a single meal order for a resident, including: * - Date and meal type (breakfast, lunch, dinner) * - Status tracking (pending, preparing, prepared) * - Meal-specific options from the paper forms * * Multi-tenant: each order belongs to a specific care home. */ export const MealOrders: CollectionConfig = { slug: 'meal-orders', labels: { singular: 'Meal Order', plural: 'Meal Orders', }, admin: { useAsTitle: 'title', description: 'Manage meal orders for residents', defaultColumns: ['title', 'resident', 'date', 'mealType', 'status'], group: 'Meal Planning', }, endpoints: [kitchenReportEndpoint], hooks: { beforeChange: [setCreatedBy], beforeValidate: [generateTitle], }, access: { // Admin and caregiver can create orders create: ({ req }) => { if (!req.user) return false if (isSuperAdmin(req.user)) return true return hasTenantRole(req.user, 'admin') || hasTenantRole(req.user, 'caregiver') }, // All authenticated users within the tenant can read orders read: ({ req }) => { if (!req.user) return false return true // Multi-tenant plugin will filter by tenant }, // Admin can update all, caregiver can update own pending orders, kitchen can update status update: ({ req }) => { if (!req.user) return false if (isSuperAdmin(req.user)) return true // All tenant roles can update (with field-level restrictions) return ( hasTenantRole(req.user, 'admin') || hasTenantRole(req.user, 'caregiver') || hasTenantRole(req.user, 'kitchen') ) }, // Only admin can delete orders delete: ({ req }) => { if (!req.user) return false if (isSuperAdmin(req.user)) return true return hasTenantRole(req.user, 'admin') }, }, fields: [ // Core Fields { name: 'title', type: 'text', admin: { readOnly: true, description: 'Auto-generated title', }, }, { name: 'resident', type: 'relationship', relationTo: 'residents', required: true, index: true, admin: { description: 'Select the resident for this meal order', }, }, { type: 'row', fields: [ { name: 'date', type: 'date', required: true, index: true, admin: { date: { pickerAppearance: 'dayOnly', displayFormat: 'yyyy-MM-dd', }, width: '50%', }, }, { name: 'mealType', type: 'select', required: true, index: true, options: [ { label: 'Breakfast (Frühstück)', value: 'breakfast' }, { label: 'Lunch (Mittagessen)', value: 'lunch' }, { label: 'Dinner (Abendessen)', value: 'dinner' }, ], admin: { width: '50%', }, }, ], }, { name: 'status', type: 'select', required: true, defaultValue: 'pending', index: true, options: [ { label: 'Pending', value: 'pending' }, { label: 'Preparing', value: 'preparing' }, { label: 'Prepared', value: 'prepared' }, ], admin: { position: 'sidebar', description: 'Order status for kitchen tracking', }, }, { name: 'createdBy', type: 'relationship', relationTo: 'users', admin: { position: 'sidebar', readOnly: true, description: 'User who created this order', }, }, // Override Fields (optional per-order overrides) { type: 'collapsible', label: 'Order Overrides', admin: { initCollapsed: true, }, fields: [ { name: 'highCaloric', type: 'checkbox', defaultValue: false, admin: { description: 'Override: high-caloric requirement for this order', }, }, { name: 'aversions', type: 'textarea', admin: { description: 'Override: specific aversions for this order', }, }, { name: 'notes', type: 'textarea', admin: { description: 'Special notes for this order', }, }, ], }, // ============================================ // BREAKFAST FIELDS GROUP // ============================================ { type: 'group', name: 'breakfast', label: 'Breakfast Options (Frühstück)', admin: { condition: (data) => data?.mealType === 'breakfast', }, fields: [ { name: 'accordingToPlan', type: 'checkbox', label: 'According to Plan (Frühstück lt. Plan)', defaultValue: false, }, { type: 'row', fields: [ { type: 'group', name: 'bread', label: 'Bread Selection', fields: [ { name: 'breadRoll', type: 'checkbox', label: 'Bread Roll (Brötchen)' }, { name: 'wholeGrainRoll', type: 'checkbox', label: 'Whole Grain Roll (Vollkornbrötchen)', }, { name: 'greyBread', type: 'checkbox', label: 'Grey Bread (Graubrot)' }, { name: 'wholeGrainBread', type: 'checkbox', label: 'Whole Grain Bread (Vollkornbrot)', }, { name: 'whiteBread', type: 'checkbox', label: 'White Bread (Weißbrot)' }, { name: 'crispbread', type: 'checkbox', label: 'Crispbread (Knäckebrot)' }, ], }, ], }, { name: 'porridge', type: 'checkbox', label: 'Porridge/Puree (Brei)', }, { type: 'row', fields: [ { type: 'group', name: 'preparation', label: 'Bread Preparation', fields: [ { name: 'sliced', type: 'checkbox', label: 'Sliced (geschnitten)' }, { name: 'spread', type: 'checkbox', label: 'Spread (geschmiert)' }, ], }, ], }, { type: 'row', fields: [ { type: 'group', name: 'spreads', label: 'Spreads', fields: [ { name: 'butter', type: 'checkbox', label: 'Butter' }, { name: 'margarine', type: 'checkbox', label: 'Margarine' }, { name: 'jam', type: 'checkbox', label: 'Jam (Konfitüre)' }, { name: 'diabeticJam', type: 'checkbox', label: 'Diabetic Jam (Diab. Konfitüre)' }, { name: 'honey', type: 'checkbox', label: 'Honey (Honig)' }, { name: 'cheese', type: 'checkbox', label: 'Cheese (Käse)' }, { name: 'quark', type: 'checkbox', label: 'Quark' }, { name: 'sausage', type: 'checkbox', label: 'Sausage (Wurst)' }, ], }, ], }, { type: 'row', fields: [ { type: 'group', name: 'beverages', label: 'Beverages', fields: [ { name: 'coffee', type: 'checkbox', label: 'Coffee (Kaffee)' }, { name: 'tea', type: 'checkbox', label: 'Tea (Tee)' }, { name: 'hotMilk', type: 'checkbox', label: 'Hot Milk (Milch heiß)' }, { name: 'coldMilk', type: 'checkbox', label: 'Cold Milk (Milch kalt)' }, ], }, ], }, { type: 'row', fields: [ { type: 'group', name: 'additions', label: 'Additions', fields: [ { name: 'sugar', type: 'checkbox', label: 'Sugar (Zucker)' }, { name: 'sweetener', type: 'checkbox', label: 'Sweetener (Süßstoff)' }, { name: 'coffeeCreamer', type: 'checkbox', label: 'Coffee Creamer (Kaffeesahne)' }, ], }, ], }, ], }, // ============================================ // LUNCH FIELDS GROUP // ============================================ { type: 'group', name: 'lunch', label: 'Lunch Options (Mittagessen)', admin: { condition: (data) => data?.mealType === 'lunch', }, fields: [ { name: 'portionSize', type: 'select', label: 'Portion Size', options: [ { label: 'Small Portion (Kleine Portion)', value: 'small' }, { label: 'Large Portion (Große Portion)', value: 'large' }, { label: 'Vegetarian Whole-Food (Vollwertkost vegetarisch)', value: 'vegetarian', }, ], }, { type: 'row', fields: [ { name: 'soup', type: 'checkbox', label: 'Soup (Suppe)', admin: { width: '50%' } }, { name: 'dessert', type: 'checkbox', label: 'Dessert', admin: { width: '50%' } }, ], }, { type: 'group', name: 'specialPreparations', label: 'Special Preparations', fields: [ { name: 'pureedFood', type: 'checkbox', label: 'Pureed Food (passierte Kost)' }, { name: 'pureedMeat', type: 'checkbox', label: 'Pureed Meat (passiertes Fleisch)' }, { name: 'slicedMeat', type: 'checkbox', label: 'Sliced Meat (geschnittenes Fleisch)' }, { name: 'mashedPotatoes', type: 'checkbox', label: 'Mashed Potatoes (Kartoffelbrei)' }, ], }, { type: 'group', name: 'restrictions', label: 'Restrictions', fields: [ { name: 'noFish', type: 'checkbox', label: 'No Fish (ohne Fisch)' }, { name: 'fingerFood', type: 'checkbox', label: 'Finger Food' }, { name: 'onlySweet', type: 'checkbox', label: 'Only Sweet (nur süß)' }, ], }, ], }, // ============================================ // DINNER FIELDS GROUP // ============================================ { type: 'group', name: 'dinner', label: 'Dinner Options (Abendessen)', admin: { condition: (data) => data?.mealType === 'dinner', }, fields: [ { name: 'accordingToPlan', type: 'checkbox', label: 'According to Plan (Abendessen lt. Plan)', defaultValue: false, }, { type: 'group', name: 'bread', label: 'Bread Selection', fields: [ { name: 'greyBread', type: 'checkbox', label: 'Grey Bread (Graubrot)' }, { name: 'wholeGrainBread', type: 'checkbox', label: 'Whole Grain Bread (Vollkornbrot)', }, { name: 'whiteBread', type: 'checkbox', label: 'White Bread (Weißbrot)' }, { name: 'crispbread', type: 'checkbox', label: 'Crispbread (Knäckebrot)' }, ], }, { type: 'group', name: 'preparation', label: 'Bread Preparation', fields: [ { name: 'spread', type: 'checkbox', label: 'Spread (geschmiert)' }, { name: 'sliced', type: 'checkbox', label: 'Sliced (geschnitten)' }, ], }, { type: 'group', name: 'spreads', label: 'Spreads', fields: [ { name: 'butter', type: 'checkbox', label: 'Butter' }, { name: 'margarine', type: 'checkbox', label: 'Margarine' }, ], }, { type: 'row', fields: [ { name: 'soup', type: 'checkbox', label: 'Soup (Suppe)', admin: { width: '33%' } }, { name: 'porridge', type: 'checkbox', label: 'Porridge (Brei)', admin: { width: '33%' }, }, { name: 'noFish', type: 'checkbox', label: 'No Fish (ohne Fisch)', admin: { width: '33%' }, }, ], }, { type: 'group', name: 'beverages', label: 'Beverages', fields: [ { name: 'tea', type: 'checkbox', label: 'Tea (Tee)' }, { name: 'cocoa', type: 'checkbox', label: 'Cocoa (Kakao)' }, { name: 'hotMilk', type: 'checkbox', label: 'Hot Milk (Milch heiß)' }, { name: 'coldMilk', type: 'checkbox', label: 'Cold Milk (Milch kalt)' }, ], }, { type: 'group', name: 'additions', label: 'Additions', fields: [ { name: 'sugar', type: 'checkbox', label: 'Sugar (Zucker)' }, { name: 'sweetener', type: 'checkbox', label: 'Sweetener (Süßstoff)' }, ], }, ], }, ], }