@@ -2,9 +2,10 @@ import React, { useState } from 'react';
2
2
import { useNavigate } from '@remix-run/react' ;
3
3
import Cookies from 'js-cookie' ;
4
4
import { toast } from 'react-toastify' ;
5
- import { db , deleteById , getAll } from '~/lib/persistence' ;
5
+ import { db , deleteById , getAll , setMessages } from '~/lib/persistence' ;
6
6
import { logStore } from '~/lib/stores/logs' ;
7
7
import { classNames } from '~/utils/classNames' ;
8
+ import type { Message } from 'ai' ;
8
9
9
10
// List of supported providers that can have API keys
10
11
const API_KEY_PROVIDERS = [
@@ -232,6 +233,76 @@ export default function DataTab() {
232
233
event . target . value = '' ;
233
234
} ;
234
235
236
+ const processChatData = ( data : any ) : Array < {
237
+ id : string ;
238
+ messages : Message [ ] ;
239
+ description : string ;
240
+ urlId ?: string ;
241
+ } > => {
242
+ // Handle Bolt standard format (single chat)
243
+ if ( data . messages && Array . isArray ( data . messages ) ) {
244
+ const chatId = crypto . randomUUID ( ) ;
245
+ return [ {
246
+ id : chatId ,
247
+ messages : data . messages ,
248
+ description : data . description || 'Imported Chat' ,
249
+ urlId : chatId
250
+ } ] ;
251
+ }
252
+
253
+ // Handle Bolt export format (multiple chats)
254
+ if ( data . chats && Array . isArray ( data . chats ) ) {
255
+ return data . chats . map ( ( chat : { id ?: string ; messages : Message [ ] ; description ?: string ; urlId ?: string ; } ) => ( {
256
+ id : chat . id || crypto . randomUUID ( ) ,
257
+ messages : chat . messages ,
258
+ description : chat . description || 'Imported Chat' ,
259
+ urlId : chat . urlId ,
260
+ } ) ) ;
261
+ }
262
+
263
+ console . error ( 'No matching format found for:' , data ) ;
264
+ throw new Error ( 'Unsupported chat format' ) ;
265
+ } ;
266
+
267
+ const handleImportChats = ( ) => {
268
+ const input = document . createElement ( 'input' ) ;
269
+ input . type = 'file' ;
270
+ input . accept = '.json' ;
271
+
272
+ input . onchange = async ( e ) => {
273
+ const file = ( e . target as HTMLInputElement ) . files ?. [ 0 ] ;
274
+
275
+ if ( ! file || ! db ) {
276
+ toast . error ( 'Something went wrong' ) ;
277
+ return ;
278
+ }
279
+
280
+ try {
281
+ const content = await file . text ( ) ;
282
+ const data = JSON . parse ( content ) ;
283
+ const chatsToImport = processChatData ( data ) ;
284
+
285
+ for ( const chat of chatsToImport ) {
286
+ await setMessages ( db , chat . id , chat . messages , chat . urlId , chat . description ) ;
287
+ }
288
+
289
+ logStore . logSystem ( 'Chats imported successfully' , { count : chatsToImport . length } ) ;
290
+ toast . success ( `Successfully imported ${ chatsToImport . length } chat${ chatsToImport . length > 1 ? 's' : '' } ` ) ;
291
+ window . location . reload ( ) ;
292
+ } catch ( error ) {
293
+ if ( error instanceof Error ) {
294
+ logStore . logError ( 'Failed to import chats:' , error ) ;
295
+ toast . error ( 'Failed to import chats: ' + error . message ) ;
296
+ } else {
297
+ toast . error ( 'Failed to import chats' ) ;
298
+ }
299
+ console . error ( error ) ;
300
+ }
301
+ } ;
302
+
303
+ input . click ( ) ;
304
+ } ;
305
+
235
306
return (
236
307
< div className = "p-4 bg-bolt-elements-bg-depth-2 border border-bolt-elements-borderColor rounded-lg mb-4" >
237
308
< div className = "mb-6" >
@@ -248,6 +319,12 @@ export default function DataTab() {
248
319
>
249
320
Export All Chats
250
321
</ button >
322
+ < button
323
+ onClick = { handleImportChats }
324
+ className = "px-4 py-2 bg-bolt-elements-button-primary-background hover:bg-bolt-elements-button-primary-backgroundHover text-bolt-elements-textPrimary rounded-lg transition-colors"
325
+ >
326
+ Import Chats
327
+ </ button >
251
328
< button
252
329
onClick = { handleDeleteAllChats }
253
330
disabled = { isDeleting }
0 commit comments