lms/resources/js/lib/page.ts
2025-12-15 12:26:23 +01:00

260 lines
7.7 KiB
TypeScript

export const getPageSection = (page: Page, slug: string) => {
return page.sections.find((section: PageSection) => section.slug === slug);
};
const formatLabel = (key: string): string => {
return key.replace(/_/g, ' ').replace(/\b\w/g, (l) => l.toUpperCase());
};
/**
* Generate a field definition based on a value's type
*/
const generateFieldByType = (key: string, value: any): PropertyField => {
// Handle different value types
if (typeof value === 'string') {
// Image or URL fields
if (key === 'image' || key.includes('image') || key === 'avatar' || (typeof value === 'string' && value.match(/\.(jpeg|jpg|gif|png)$/i))) {
return {
type: 'file',
label: formatLabel(key),
name: key,
value: null,
};
// Description fields
} else if (key === 'description' || key.includes('description') || key === 'bio' || key === 'content') {
return {
type: 'textarea',
label: formatLabel(key),
name: key,
value: value || '',
};
// URL fields
} else if (key === 'url' || key.includes('_url') || key.includes('link')) {
return {
type: 'url',
label: formatLabel(key),
name: key,
value: value || '',
};
// Default string field
} else if (key === 'icon') {
return {
type: 'icon',
label: formatLabel(key),
name: key,
value: value || '',
};
} else {
return {
type: 'text',
label: formatLabel(key),
name: key,
value: value || '',
};
}
} else if (typeof value === 'number') {
return {
type: 'number',
label: formatLabel(key),
name: key,
value: value || 0,
};
} else if (typeof value === 'boolean') {
return {
type: 'boolean',
label: formatLabel(key),
name: key,
value: value || false,
};
} else {
// Default to text field for other types
return {
type: 'text',
label: formatLabel(key),
name: key,
value: value?.toString() || '',
};
}
};
/**
* Generate fields based only on properties object without section name dependency
* @param properties - The section properties object
* @returns Array of property fields
*/
export const generatePropertyFields = (properties: Record<string, any>): PropertyField[] => {
// Handle contents property (dynamic content from database)
if ('contents' in properties) {
const fields: PropertyField[] = [
{
type: 'contents',
label: 'Contents',
name: 'contents',
value: properties.contents || [],
},
];
// Add other properties to the beginning of fields array
Object.entries(properties).forEach(([key, value]) => {
// Skip array property as it's already handled
if (key === 'array') {
return;
}
// Skip contents property as it's handled separately
if (key === 'contents') {
return;
}
// Generate field for other properties
const field = generateFieldByType(key, value);
fields.unshift(field);
});
return fields;
}
// Handle array property (static content defined in seeder)
if ('array' in properties) {
// const sampleItem =
// Array.isArray(properties.array) && properties.array.length > 0
// ? isEmptyArrayItem(properties.array[0])
// ? properties.array[1]
// : properties.array[0]
// : {};
const sampleItem = Array.isArray(properties.array) && properties.array.length > 0 ? properties.array[0] : {};
let itemFields: PropertyField[] = [];
// Generate fields based on sample item if available
if (Object.keys(sampleItem).length > 0) {
Object.keys(sampleItem).forEach((key) => {
if (typeof sampleItem[key] === 'string' || typeof sampleItem[key] === 'number' || typeof sampleItem[key] === 'boolean') {
itemFields.push(generateFieldByType(key, sampleItem[key]));
}
});
} else {
// Default basic fields if no sample available
itemFields = [
{ type: 'text' as const, label: 'Title', name: 'title', value: '' },
{ type: 'text' as const, label: 'Value', name: 'value', value: '' },
];
}
// Return the array field
const fields: PropertyField[] = [
{
type: 'array',
label: 'Items',
name: 'array',
value: (properties.array || []).map((item: Record<string, any>) => {
// Process each item in the array to set image fields to null
const processedItem = { ...item };
// Set image properties to null for file uploads
Object.keys(processedItem).forEach((key) => {
if (key === 'image' || key.includes('image')) {
processedItem[`new_image`] = null;
}
});
return processedItem;
}),
fields: itemFields,
},
];
// Add other properties to the beginning of fields array
Object.entries(properties).forEach(([key, value]) => {
// Skip array property as it's already handled
if (key === 'array') {
return;
}
// Skip contents property as it's handled separately
if (key === 'contents') {
return;
}
// Generate field for other properties
const field = generateFieldByType(key, value);
fields.unshift(field);
});
return fields;
}
// Process all other properties
return Object.entries(properties).map(([key, value]) => {
if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
// Handle nested objects
return {
type: 'object',
label: formatLabel(key),
name: key,
value: value,
fields: generatePropertyFields(value),
} as PropertyField;
} else {
// Use the common field generation function for primitive types
return generateFieldByType(key, value);
}
});
};
export const isEmptyArrayItem = (array: Record<string, any>) => {
let flag = true;
Object.entries(array).forEach(([key, value]) => {
switch (typeof value) {
case 'string':
if (value.length > 0) {
flag = false;
}
break;
case 'number':
if (value > 0) {
flag = false;
}
break;
default:
break;
}
});
return flag;
};
export const removeEmptyArrayItems = (array: Record<string, any>[]) => {
return array.filter((item) => {
let flag = false;
Object.entries(item).forEach(([key, value]) => {
switch (typeof value) {
case 'string':
if (value.trim().length > 0) {
flag = true;
}
break;
case 'number':
if (value > 0) {
flag = true;
}
break;
case 'boolean':
if (value) {
flag = true;
}
break;
default:
break;
}
});
return flag;
});
};
export const getPropertyArray = (section?: PageSection) => {
const array = section?.properties?.array;
return array ? removeEmptyArrayItems(array) : [];
};