@ai-stack/payloadcms
Version:
<p align="center"> <img alt="Payload AI Plugin" src="assets/payload-ai-intro.gif" width="100%" /> </p>
120 lines (119 loc) • 4.96 kB
JavaScript
export const updateFieldsConfig = (collectionConfig)=>{
let schemaPathMap = {};
/**
* Recursively updates field configuration to inject AI components
* @param field - Payload field to process
* @param parentPath - Schema path from parent (for nested fields)
*/ function updateField(field, parentPath = '') {
const fieldWithName = field;
const currentPath = parentPath ? `${parentPath}.${fieldWithName.name}` : fieldWithName.name;
const currentSchemaPath = `${collectionConfig.slug}.${currentPath}`;
// Disabled fields/ field types - skip processing
const admin = field.admin;
if (admin?.disabled || admin?.readOnly || admin?.hidden) {
return field;
}
// Rows are layout-only constructs and should not add to the schema path (like tabs)
// Process their nested fields but use parentPath to maintain correct path structure
if (field.type === 'row' && 'fields' in field) {
const fieldWithFields = field;
return {
...field,
fields: fieldWithFields.fields.map((subField)=>updateField(subField, parentPath))
};
}
// Map field path for global fieldInstructionsMap to load related instructions
// This is done due to save extra API call to get instructions when Field components are loaded in admin
// Doing is will only call instructions data when user clicks on settings
if (field.type && [
'richText',
'text',
'textarea',
'upload'
].includes(field.type)) {
const fieldAny = field;
schemaPathMap = {
...schemaPathMap,
[currentSchemaPath]: {
type: field.type,
label: fieldAny.label || fieldWithName.name,
relationTo: fieldAny.relationTo
}
};
}
// Inject AI actions, richText is not included here as it has to be explicitly defined by user
if (field.type && [
'text',
'textarea',
'upload'
].includes(field.type)) {
const customField = {};
// Custom fields don't fully adhere to the Payload schema, making it difficult to
// determine which components support injecting ComposeField as a Description.
if (admin?.components) {
// TODO: If a field already provides its own Description, we still inject our ComposeField
// by overriding Description. If you need both, consider composing your own wrapper.
// customField would be used here if needed
}
return {
...field,
admin: {
...field.admin,
components: {
...admin?.components || {},
Description: {
clientProps: {
schemaPath: currentSchemaPath
},
path: '@ai-stack/payloadcms/client#ComposeField'
},
...customField
}
}
};
}
// Handle fields with nested fields (group, collapsible, etc.)
if ('fields' in field && Array.isArray(field.fields)) {
const fieldWithFields = field;
return {
...field,
fields: fieldWithFields.fields.map((subField)=>updateField(subField, currentPath))
};
}
// Handle fields with tabs
if ('tabs' in field && Array.isArray(field.tabs)) {
const fieldWithTabs = field;
return {
...field,
tabs: fieldWithTabs.tabs.map((tab)=>{
return {
...tab,
// Tabs are a UI construct and should not add to the schema path
fields: (tab.fields || []).map((subField)=>updateField(subField, parentPath))
};
})
};
}
// Handle fields with blocks
if ('blocks' in field && Array.isArray(field.blocks)) {
const fieldWithBlocks = field;
return {
...field,
blocks: fieldWithBlocks.blocks.map((block)=>({
...block,
fields: block.fields.map((subField)=>updateField(subField, `${currentPath}.${block.slug}`))
}))
};
}
return field;
}
const updatedCollectionConfig = {
...collectionConfig,
fields: collectionConfig.fields.map((field)=>updateField(field))
};
return {
schemaPathMap,
updatedCollectionConfig
};
};
//# sourceMappingURL=updateFieldsConfig.js.map