@@ -22,12 +22,14 @@ interface Props {
22
22
initialData? : Partial <McpServerFormData >
23
23
submitButtonText? : string
24
24
cancelButtonText? : string
25
+ serverId? : string
25
26
}
26
27
27
28
const props = withDefaults (defineProps <Props >(), {
28
29
mode: ' create' ,
29
30
submitButtonText: ' ' ,
30
- cancelButtonText: ' '
31
+ cancelButtonText: ' ' ,
32
+ serverId: ' '
31
33
})
32
34
33
35
// Emits
@@ -41,6 +43,10 @@ const emit = defineEmits<{
41
43
const { t } = useI18n ()
42
44
const eventBus = useEventBus ()
43
45
46
+ // Storage key for form drafts
47
+ const FORM_DRAFTS_KEY = ' mcp_edit_drafts'
48
+ const DRAFT_EXPIRY_HOURS = 24
49
+
44
50
// Form steps configuration
45
51
const steps = [
46
52
{
@@ -155,7 +161,8 @@ const formData = ref<McpServerFormData>({
155
161
author_contact: ' ' ,
156
162
organization: ' ' ,
157
163
license: ' ' ,
158
- tags: []
164
+ tags: [],
165
+ featured: false
159
166
},
160
167
repository: {
161
168
github_url: ' ' ,
@@ -337,7 +344,8 @@ const autoPopulateFromGitHub = (githubData: any) => {
337
344
author_contact: githubData .owner ?.email || githubData .author_contact || ' ' ,
338
345
organization: githubData .owner ?.type === ' Organization' ? githubData .owner .login : (githubData .organization || ' ' ),
339
346
license: githubData .license ?.spdx_id || githubData .license || ' ' ,
340
- tags: githubData .topics || githubData .tags || []
347
+ tags: githubData .topics || githubData .tags || [],
348
+ featured: formData .value .basic .featured // Keep user selection for featured status
341
349
},
342
350
repository: {
343
351
github_url: githubData .html_url || githubData .github_url || formData .value .github .github_url ,
@@ -404,20 +412,118 @@ const parseInstallationMethods = (githubData: any): string[] => {
404
412
return methods .length > 0 ? methods : [' manual' ]
405
413
}
406
414
407
- // Form persistence using event bus
408
- const saveFormData = () => {
409
- eventBus .emit (' mcp-form-data-updated' , {
410
- step: currentStep .value ,
411
- data: formData .value
415
+ // Draft management functions with proper typing
416
+ interface FormDraft {
417
+ data: McpServerFormData
418
+ lastModified: string
419
+ currentStep: number
420
+ }
421
+
422
+ interface FormDrafts {
423
+ [serverId : string ]: FormDraft
424
+ }
425
+
426
+ const getDrafts = (): FormDrafts => {
427
+ return eventBus .getState <FormDrafts >(FORM_DRAFTS_KEY , {}) || {}
428
+ }
429
+
430
+ const saveDraft = () => {
431
+ if (! props .serverId ) return
432
+
433
+ const drafts = getDrafts ()
434
+ drafts [props .serverId ] = {
435
+ data: formData .value ,
436
+ lastModified: new Date ().toISOString (),
437
+ currentStep: currentStep .value
438
+ }
439
+
440
+ eventBus .setState (FORM_DRAFTS_KEY , drafts )
441
+
442
+ // Emit specific events for real-time updates
443
+ eventBus .emit (' mcp-edit-draft-updated' , {
444
+ serverId: props .serverId ,
445
+ data: formData .value ,
446
+ step: currentStep .value
412
447
})
413
448
}
414
449
415
- const loadFormData = () => {
416
- // Try to load persisted form data
417
- // This would be implemented with localStorage or session storage
418
- // For now, we'll keep the default empty form
450
+ const loadDraft = (): boolean => {
451
+ if (! props .serverId ) return false
452
+
453
+ const drafts = getDrafts ()
454
+ const draft = drafts [props .serverId ]
455
+
456
+ if (draft ) {
457
+ // Check if draft is not expired
458
+ const draftAge = Date .now () - new Date (draft .lastModified ).getTime ()
459
+ const maxAge = DRAFT_EXPIRY_HOURS * 60 * 60 * 1000
460
+
461
+ if (draftAge < maxAge ) {
462
+ formData .value = draft .data
463
+ currentStep .value = draft .currentStep || 0
464
+ return true
465
+ } else {
466
+ // Remove expired draft
467
+ clearDraft ()
468
+ }
469
+ }
470
+
471
+ return false
472
+ }
473
+
474
+ const clearDraft = () => {
475
+ if (! props .serverId ) return
476
+
477
+ const drafts = getDrafts ()
478
+ if (drafts [props .serverId ]) {
479
+ delete drafts [props .serverId ]
480
+ eventBus .setState (FORM_DRAFTS_KEY , drafts )
481
+
482
+ eventBus .emit (' mcp-edit-draft-cleared' , {
483
+ serverId: props .serverId
484
+ })
485
+ }
486
+ }
487
+
488
+ const cleanupExpiredDrafts = () => {
489
+ const drafts = getDrafts ()
490
+ const maxAge = DRAFT_EXPIRY_HOURS * 60 * 60 * 1000
491
+ let hasChanges = false
492
+
493
+ Object .keys (drafts ).forEach (serverId => {
494
+ const draft = drafts [serverId ]
495
+ if (draft && draft .lastModified ) {
496
+ const draftAge = Date .now () - new Date (draft .lastModified ).getTime ()
497
+
498
+ if (draftAge >= maxAge ) {
499
+ delete drafts [serverId ]
500
+ hasChanges = true
501
+ }
502
+ }
503
+ })
504
+
505
+ if (hasChanges ) {
506
+ eventBus .setState (FORM_DRAFTS_KEY , drafts )
507
+ }
419
508
}
420
509
510
+ // Enhanced form data watcher for real-time storage
511
+ watch (
512
+ formData ,
513
+ () => {
514
+ saveDraft ()
515
+ },
516
+ { deep: true }
517
+ )
518
+
519
+ // Watch current step changes
520
+ watch (
521
+ currentStep ,
522
+ () => {
523
+ saveDraft ()
524
+ }
525
+ )
526
+
421
527
// Form submission
422
528
const submitForm = async () => {
423
529
try {
@@ -436,11 +542,28 @@ const submitForm = async () => {
436
542
437
543
// Lifecycle
438
544
onMounted (() => {
439
- loadFormData ()
545
+ // Clean up expired drafts on mount
546
+ cleanupExpiredDrafts ()
547
+
548
+ // In edit mode, check for existing draft and clear it before loading initial data
549
+ if (props .mode === ' edit' && props .serverId ) {
550
+ clearDraft () // Clear any existing draft for this server
551
+ }
552
+
553
+ // Try to load draft (mainly for create mode or if user refreshed page)
554
+ const draftLoaded = loadDraft ()
555
+
556
+ // If no draft was loaded and we have initial data, use it
557
+ if (! draftLoaded && props .initialData ) {
558
+ // The initialData watcher will handle this
559
+ }
440
560
})
441
561
442
562
onUnmounted (() => {
443
- saveFormData ()
563
+ // Save current state as draft when component unmounts (unless submitting)
564
+ if (! isSubmitting .value ) {
565
+ saveDraft ()
566
+ }
444
567
})
445
568
</script >
446
569
@@ -478,6 +601,7 @@ onUnmounted(() => {
478
601
v-else
479
602
v-model =" formData[currentStepData.key]"
480
603
:form-data =" formData"
604
+ @update:modelValue =" (newValue: any) => formData[currentStepData.key] = newValue"
481
605
/>
482
606
</div >
483
607
0 commit comments