@@ -13,11 +13,11 @@ import {
1313} from '@/components/ui/dialog'
1414import { Button } from '@/components/ui/button'
1515import { api , ApiError } from '@/lib/api'
16- import { parseMongoQuery } from '@/lib/mongoQueryLang'
16+ import { parseMongoDocuments , parseMongoQuery } from '@/lib/mongoQueryLang'
1717import { serializeMongoValue } from '@/lib/mongoQuerySerialize'
1818import type { DocumentEnvelope , UuidEncoding } from '@shared/types'
1919
20- export type EditorMode = 'view' | 'edit' | 'duplicate' | 'insert'
20+ export type EditorMode = 'view' | 'edit' | 'duplicate' | 'insert' | 'insert-many'
2121
2222type Props = {
2323 mode : EditorMode | null
@@ -36,18 +36,22 @@ const TITLES: Record<EditorMode, string> = {
3636 view : 'View document' ,
3737 edit : 'Edit document' ,
3838 duplicate : 'Duplicate document' ,
39- insert : 'Insert document'
39+ insert : 'Insert document' ,
40+ 'insert-many' : 'Insert documents'
4041}
4142
4243const DESCRIPTIONS : Record < EditorMode , string > = {
4344 view : 'Read-only — BSON types render as ObjectId / ISODate / UUID / NumberLong / NumberDecimal.' ,
4445 edit : 'Edit using mongo shell syntax — ObjectId("…"), ISODate("…"), NumberLong("…"), UUID/JUUID, regex literals.' ,
4546 duplicate : 'A copy with the original _id removed. Save inserts a new document with a fresh _id.' ,
4647 insert :
47- 'New document — mongo shell syntax. Leave _id out and the server will assign a fresh ObjectId.'
48+ 'New document — mongo shell syntax. Leave _id out and the server will assign a fresh ObjectId.' ,
49+ 'insert-many' :
50+ 'Multiple documents, one after another newline, comma-separated or JSON array. Leave _id out for a fresh ObjectId.'
4851}
4952
5053const INSERT_TEMPLATE = '{\n \n}'
54+ const INSERT_MANY_TEMPLATE = '{\n \n}\n{\n \n}'
5155
5256/**
5357 * Read the canonical EJSON document carried by an envelope, then re-render
@@ -95,7 +99,7 @@ export function DocumentEditorDialog({
9599 timezone,
96100 onClose
97101} : Props ) {
98- const open = mode !== null && ( mode === 'insert' || envelope !== null )
102+ const open = mode !== null && ( mode === 'insert' || mode === 'insert-many' || envelope !== null )
99103 const [ value , setValue ] = useState ( '' )
100104 const [ serverError , setServerError ] = useState < string | null > ( null )
101105 const queryClient = useQueryClient ( )
@@ -107,21 +111,35 @@ export function DocumentEditorDialog({
107111 setValue ( INSERT_TEMPLATE )
108112 return
109113 }
114+ if ( mode === 'insert-many' ) {
115+ setValue ( INSERT_MANY_TEMPLATE )
116+ return
117+ }
110118 if ( ! envelope ) return
111119 const rendered = shellRenderOf ( envelope , uuidEncoding , timezone )
112120 setValue ( mode === 'duplicate' ? stripIdShell ( rendered , uuidEncoding , timezone ) : rendered )
113121 } , [ envelope , mode , uuidEncoding , timezone ] )
114122
115- const compiled = useMemo ( ( ) => {
116- if ( mode === 'view' || mode === null ) return { ok : true as const , ejson : '' }
123+ const compiled = useMemo <
124+ { ok : true ; ejson : string ; documents : string [ ] } | { ok : false ; error : string }
125+ > ( ( ) => {
126+ if ( mode === 'view' || mode === null ) return { ok : true , ejson : '' , documents : [ ] }
127+ if ( mode === 'insert-many' ) {
128+ const parsed = parseMongoDocuments ( value )
129+ if ( ! parsed . ok ) return { ok : false , error : parsed . error }
130+ if ( parsed . documents . length === 0 ) return { ok : false , error : 'Enter at least one document' }
131+ return { ok : true , ejson : '' , documents : parsed . documents }
132+ }
117133 const parsed = parseMongoQuery ( value )
118- if ( ! parsed . ok ) return { ok : false as const , error : parsed . error }
134+ if ( ! parsed . ok ) return { ok : false , error : parsed . error }
119135 if ( parsed . value === null || typeof parsed . value !== 'object' || Array . isArray ( parsed . value ) ) {
120- return { ok : false as const , error : 'Document must be an object' }
136+ return { ok : false , error : 'Document must be an object' }
121137 }
122- return { ok : true as const , ejson : parsed . ejson }
138+ return { ok : true , ejson : parsed . ejson , documents : [ ] }
123139 } , [ value , mode ] )
124140
141+ const manyCount = compiled . ok ? compiled . documents . length : 0
142+
125143 const saveMutation = useMutation ( {
126144 mutationFn : async ( ) => {
127145 if ( ! mode ) throw new Error ( 'No editor mode' )
@@ -137,6 +155,9 @@ export function DocumentEditorDialog({
137155 replacement : compiled . ejson
138156 } )
139157 }
158+ if ( mode === 'insert-many' ) {
159+ return api . query . insertMany ( { connectionId, db, coll, documents : compiled . documents } )
160+ }
140161 if ( mode === 'duplicate' || mode === 'insert' ) {
141162 return api . query . insertOne ( { connectionId, db, coll, document : compiled . ejson } )
142163 }
@@ -150,7 +171,9 @@ export function DocumentEditorDialog({
150171 ? 'Document updated'
151172 : mode === 'duplicate'
152173 ? 'Document duplicated'
153- : 'Document inserted'
174+ : mode === 'insert-many'
175+ ? `Inserted ${ manyCount } document${ manyCount === 1 ? '' : 's' } `
176+ : 'Document inserted'
154177 toast . success ( successMessage )
155178 onClose ( )
156179 } ,
@@ -167,12 +190,20 @@ export function DocumentEditorDialog({
167190 if ( ! mode ) return null
168191
169192 // Insert needs neither envelope nor _id; the others can't render without one.
170- if ( mode !== 'insert' && ! envelope ) return null
193+ if ( mode !== 'insert' && mode !== 'insert-many' && ! envelope ) return null
171194
172195 const parseError = compiled . ok ? null : compiled . error
173196 const isReadOnly = mode === 'view'
174197 const ctaLabel =
175- mode === 'edit' ? 'Save changes' : mode === 'duplicate' ? 'Insert duplicate' : 'Insert document'
198+ mode === 'edit'
199+ ? 'Save changes'
200+ : mode === 'duplicate'
201+ ? 'Insert duplicate'
202+ : mode === 'insert-many'
203+ ? manyCount > 0
204+ ? `Insert ${ manyCount } document${ manyCount === 1 ? '' : 's' } `
205+ : 'Insert documents'
206+ : 'Insert document'
176207
177208 return (
178209 < Dialog open = { open } onOpenChange = { handleOpenChange } >
@@ -204,7 +235,7 @@ export function DocumentEditorDialog({
204235 }
205236 >
206237 < Editor
207- key = { mode === 'insert' ? 'insert' : `${ envelope ?. id } ::${ mode } ` }
238+ key = { mode === 'insert' || mode === 'insert-many' ? mode : `${ envelope ?. id } ::${ mode } ` }
208239 height = "100%"
209240 width = "100%"
210241 value = { value }
0 commit comments