|
|
@@ -28,10 +28,15 @@ import { useTheme } from '../contexts/ThemeContext'
|
|
|
|
|
|
interface Document {
|
|
|
_id: string
|
|
|
- _createdAt?: number
|
|
|
- _updatedAt?: number
|
|
|
- _createdBy?: string
|
|
|
- _updatedBy?: string
|
|
|
+ // The database names these one fixed way, in nanoseconds. Declaring the
|
|
|
+ // camelCase spellings meant the Created/Updated panel below keyed off fields
|
|
|
+ // that never exist, so it never appeared at all.
|
|
|
+ /** Fields the listing left out because they were large. Set only in a listing. */
|
|
|
+ _omittedFields?: string[]
|
|
|
+ _created_at?: number
|
|
|
+ _updated_at?: number
|
|
|
+ _created_by?: string
|
|
|
+ _updated_by?: string
|
|
|
[key: string]: any
|
|
|
}
|
|
|
|
|
|
@@ -70,6 +75,7 @@ export default function DatabasePage() {
|
|
|
const [selectedCollection, setSelectedCollection] = useState<string | null>(null)
|
|
|
const [selectedDocuments, setSelectedDocuments] = useState<Set<string>>(new Set())
|
|
|
const [editingDocument, setEditingDocument] = useState<Document | null>(null)
|
|
|
+ const [loadingDocument, setLoadingDocument] = useState(false)
|
|
|
const [editingJson, setEditingJson] = useState('')
|
|
|
const [originalJson, setOriginalJson] = useState('')
|
|
|
const [jsonError, setJsonError] = useState<string | null>(null)
|
|
|
@@ -306,8 +312,25 @@ export default function DatabasePage() {
|
|
|
// Single selection - also open document for editing
|
|
|
setSelectedDocuments(new Set([doc._id]))
|
|
|
guardUnsavedChanges(() => {
|
|
|
- setEditingDocument(doc)
|
|
|
setShowNewDocument(false)
|
|
|
+ // The listing leaves large values out, so the row in hand may be a
|
|
|
+ // summary. Editing it as-is would show a placeholder where the content
|
|
|
+ // should be, and saving would write that placeholder over the real
|
|
|
+ // thing - so the document is fetched whole before it can be edited.
|
|
|
+ if (doc._omittedFields && selectedCollection) {
|
|
|
+ setEditingDocument(null)
|
|
|
+ setLoadingDocument(true)
|
|
|
+ databaseApi
|
|
|
+ .getDocument(selectedCollection, doc._id)
|
|
|
+ .then((full) => setEditingDocument(full))
|
|
|
+ .catch(() => {
|
|
|
+ setEditingDocument(null)
|
|
|
+ setJsonError('Could not read that document in full - it has not been opened, so nothing can overwrite it')
|
|
|
+ })
|
|
|
+ .finally(() => setLoadingDocument(false))
|
|
|
+ } else {
|
|
|
+ setEditingDocument(doc)
|
|
|
+ }
|
|
|
})
|
|
|
}
|
|
|
}
|
|
|
@@ -783,24 +806,24 @@ export default function DatabasePage() {
|
|
|
/>
|
|
|
</div>
|
|
|
{/* Document metadata */}
|
|
|
- {editingDocument && (editingDocument._createdAt || editingDocument._updatedAt) && (
|
|
|
+ {editingDocument && (editingDocument._created_at || editingDocument._updated_at) && (
|
|
|
<div className="px-4 pb-4 flex-shrink-0">
|
|
|
<div className="p-3 bg-gray-50 dark:bg-slate-700 rounded-lg text-xs text-gray-600 dark:text-gray-400 grid grid-cols-2 gap-2">
|
|
|
- {editingDocument._createdAt && (
|
|
|
+ {editingDocument._created_at && (
|
|
|
<div className="flex items-center gap-2">
|
|
|
<Clock className="w-3 h-3" />
|
|
|
- <span>Created: {formatTimestamp(editingDocument._createdAt)}</span>
|
|
|
- {editingDocument._createdBy && (
|
|
|
- <span className="text-gray-400 dark:text-gray-500">by {editingDocument._createdBy}</span>
|
|
|
+ <span>Created: {formatTimestamp(editingDocument._created_at)}</span>
|
|
|
+ {editingDocument._created_by && (
|
|
|
+ <span className="text-gray-400 dark:text-gray-500">by {editingDocument._created_by}</span>
|
|
|
)}
|
|
|
</div>
|
|
|
)}
|
|
|
- {editingDocument._updatedAt && (
|
|
|
+ {editingDocument._updated_at && (
|
|
|
<div className="flex items-center gap-2">
|
|
|
<Clock className="w-3 h-3" />
|
|
|
- <span>Updated: {formatTimestamp(editingDocument._updatedAt)}</span>
|
|
|
- {editingDocument._updatedBy && (
|
|
|
- <span className="text-gray-400 dark:text-gray-500">by {editingDocument._updatedBy}</span>
|
|
|
+ <span>Updated: {formatTimestamp(editingDocument._updated_at)}</span>
|
|
|
+ {editingDocument._updated_by && (
|
|
|
+ <span className="text-gray-400 dark:text-gray-500">by {editingDocument._updated_by}</span>
|
|
|
)}
|
|
|
</div>
|
|
|
)}
|
|
|
@@ -811,12 +834,22 @@ export default function DatabasePage() {
|
|
|
) : (
|
|
|
<div className="h-full flex items-center justify-center text-gray-500 dark:text-gray-400">
|
|
|
<div className="text-center">
|
|
|
+ {loadingDocument ? (
|
|
|
+ <>
|
|
|
+ <div className="animate-spin rounded-full h-8 w-8 border-b-2 border-primary-600 mx-auto mb-4" />
|
|
|
+ <p>Reading the whole document...</p>
|
|
|
+ <p className="text-sm">The list leaves large values out; this is fetching the real one.</p>
|
|
|
+ </>
|
|
|
+ ) : (
|
|
|
+ <>
|
|
|
<FileJson className="w-12 h-12 mx-auto mb-4 opacity-50" />
|
|
|
<p>Select a document to edit</p>
|
|
|
<p className="text-sm">or create a new one</p>
|
|
|
<p className="text-xs mt-4 text-gray-400 dark:text-gray-500">
|
|
|
Tip: Ctrl+Click to multi-select, Shift+Click for range select
|
|
|
</p>
|
|
|
+ </>
|
|
|
+ )}
|
|
|
</div>
|
|
|
</div>
|
|
|
)}
|