|
@@ -67,6 +67,15 @@ const getWidgetsForType = (fieldType: string) => {
|
|
|
return WIDGET_TYPES_BY_FIELD[fieldType] || [{ value: '', label: 'Auto' }]
|
|
return WIDGET_TYPES_BY_FIELD[fieldType] || [{ value: '', label: 'Auto' }]
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+// Built-in system fields that can be displayed in views
|
|
|
|
|
+const SYSTEM_FIELDS = [
|
|
|
|
|
+ { name: '_id', label: 'Document ID', type: 'text', description: 'Unique identifier' },
|
|
|
|
|
+ { name: '_created_at', label: 'Created At', type: 'datetime', description: 'Creation timestamp' },
|
|
|
|
|
+ { name: '_updated_at', label: 'Updated At', type: 'datetime', description: 'Last update timestamp' },
|
|
|
|
|
+ { name: '_created_by_name', label: 'Created By', type: 'text', description: 'Name of user who created' },
|
|
|
|
|
+ { name: '_updated_by_name', label: 'Updated By', type: 'text', description: 'Name of user who last updated' },
|
|
|
|
|
+] as const
|
|
|
|
|
+
|
|
|
interface SchemaField {
|
|
interface SchemaField {
|
|
|
_id: string // Client-side ID for React keys
|
|
_id: string // Client-side ID for React keys
|
|
|
name: string
|
|
name: string
|
|
@@ -395,6 +404,18 @@ function ViewModal({
|
|
|
(view?.settings?.quick_edit_fields as string[]) || []
|
|
(view?.settings?.quick_edit_fields as string[]) || []
|
|
|
)
|
|
)
|
|
|
|
|
|
|
|
|
|
+ // System fields state - built-in document metadata fields
|
|
|
|
|
+ const [enabledSystemFields, setEnabledSystemFields] = useState<string[]>(() => {
|
|
|
|
|
+ if (view?.schema?.fields) {
|
|
|
|
|
+ // Check which system fields are already in the schema
|
|
|
|
|
+ const systemFieldNames = ['_id', '_created_at', '_updated_at', '_created_by_name', '_updated_by_name']
|
|
|
|
|
+ return view.schema.fields
|
|
|
|
|
+ .filter(f => systemFieldNames.includes(f.name))
|
|
|
|
|
+ .map(f => f.name)
|
|
|
|
|
+ }
|
|
|
|
|
+ return []
|
|
|
|
|
+ })
|
|
|
|
|
+
|
|
|
// Local state for field name editing to avoid re-renders
|
|
// Local state for field name editing to avoid re-renders
|
|
|
const [editingFieldId, setEditingFieldId] = useState<string | null>(null)
|
|
const [editingFieldId, setEditingFieldId] = useState<string | null>(null)
|
|
|
const [editingFieldName, setEditingFieldName] = useState('')
|
|
const [editingFieldName, setEditingFieldName] = useState('')
|
|
@@ -482,8 +503,8 @@ function ViewModal({
|
|
|
setError(null)
|
|
setError(null)
|
|
|
|
|
|
|
|
try {
|
|
try {
|
|
|
- // Build schema in backend format
|
|
|
|
|
- const schemaFields = sortedFields.map((f) => {
|
|
|
|
|
|
|
+ // Build schema in backend format - custom fields
|
|
|
|
|
+ const customFields = sortedFields.map((f) => {
|
|
|
const field: Omit<SchemaField, '_id'> = {
|
|
const field: Omit<SchemaField, '_id'> = {
|
|
|
name: f.name,
|
|
name: f.name,
|
|
|
type: f.type,
|
|
type: f.type,
|
|
@@ -506,6 +527,23 @@ function ViewModal({
|
|
|
return field
|
|
return field
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
|
|
+ // Add enabled system fields (at the end with high display_order)
|
|
|
|
|
+ const systemFieldsToInclude = enabledSystemFields.map((fieldName, index) => {
|
|
|
|
|
+ const systemField = SYSTEM_FIELDS.find(sf => sf.name === fieldName)
|
|
|
|
|
+ return {
|
|
|
|
|
+ name: fieldName,
|
|
|
|
|
+ type: systemField?.type || 'text',
|
|
|
|
|
+ label: systemField?.label || fieldName,
|
|
|
|
|
+ description: systemField?.description || '',
|
|
|
|
|
+ required: false,
|
|
|
|
|
+ display_order: 1000 + index, // Put system fields at the end
|
|
|
|
|
+ widget: '',
|
|
|
|
|
+ group: '_system',
|
|
|
|
|
+ }
|
|
|
|
|
+ })
|
|
|
|
|
+
|
|
|
|
|
+ const schemaFields = [...customFields, ...systemFieldsToInclude]
|
|
|
|
|
+
|
|
|
const payload = {
|
|
const payload = {
|
|
|
name,
|
|
name,
|
|
|
collection_name: collectionName,
|
|
collection_name: collectionName,
|
|
@@ -601,10 +639,47 @@ function ViewModal({
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
|
|
|
|
+ {/* System Fields */}
|
|
|
|
|
+ <div className="mt-6">
|
|
|
|
|
+ <h3 className="mb-3 text-lg font-medium text-gray-900">System Fields</h3>
|
|
|
|
|
+ <p className="mb-3 text-sm text-gray-500">
|
|
|
|
|
+ Include built-in document metadata fields in your view
|
|
|
|
|
+ </p>
|
|
|
|
|
+ <div className="grid gap-2 sm:grid-cols-2 lg:grid-cols-3">
|
|
|
|
|
+ {SYSTEM_FIELDS.map((sysField) => (
|
|
|
|
|
+ <label
|
|
|
|
|
+ key={sysField.name}
|
|
|
|
|
+ className={`flex cursor-pointer items-start gap-3 rounded-lg border p-3 transition ${
|
|
|
|
|
+ enabledSystemFields.includes(sysField.name)
|
|
|
|
|
+ ? 'border-primary-500 bg-primary-50'
|
|
|
|
|
+ : 'border-gray-200 hover:border-gray-300'
|
|
|
|
|
+ }`}
|
|
|
|
|
+ >
|
|
|
|
|
+ <input
|
|
|
|
|
+ type="checkbox"
|
|
|
|
|
+ checked={enabledSystemFields.includes(sysField.name)}
|
|
|
|
|
+ onChange={(e) => {
|
|
|
|
|
+ if (e.target.checked) {
|
|
|
|
|
+ setEnabledSystemFields([...enabledSystemFields, sysField.name])
|
|
|
|
|
+ } else {
|
|
|
|
|
+ setEnabledSystemFields(enabledSystemFields.filter(f => f !== sysField.name))
|
|
|
|
|
+ }
|
|
|
|
|
+ }}
|
|
|
|
|
+ className="mt-0.5 h-4 w-4 rounded border-gray-300"
|
|
|
|
|
+ />
|
|
|
|
|
+ <div>
|
|
|
|
|
+ <span className="font-medium text-gray-900">{sysField.label}</span>
|
|
|
|
|
+ <p className="text-xs text-gray-500">{sysField.description}</p>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </label>
|
|
|
|
|
+ ))}
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
{/* Schema Designer */}
|
|
{/* Schema Designer */}
|
|
|
<div className="mt-6">
|
|
<div className="mt-6">
|
|
|
<div className="mb-3 flex items-center justify-between">
|
|
<div className="mb-3 flex items-center justify-between">
|
|
|
- <h3 className="text-lg font-medium text-gray-900">Fields</h3>
|
|
|
|
|
|
|
+ <h3 className="text-lg font-medium text-gray-900">Custom Fields</h3>
|
|
|
<Button type="button" size="sm" onClick={addField}>
|
|
<Button type="button" size="sm" onClick={addField}>
|
|
|
<svg className="-ml-1 mr-1 h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
<svg className="-ml-1 mr-1 h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 4v16m8-8H4" />
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 4v16m8-8H4" />
|