EditNotebookDrawer.tsx 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146
  1. import React, { useEffect, useState } from 'react'
  2. import { Plus, ChevronRight, Save } from 'lucide-react'
  3. import { KnowledgeGroup, UpdateGroupData } from '../types'
  4. import { useLanguage } from '../contexts/LanguageContext'
  5. interface EditNotebookDrawerProps {
  6. isOpen: boolean
  7. onClose: () => void
  8. notebook: KnowledgeGroup
  9. onUpdate: (id: string, data: UpdateGroupData) => Promise<void>
  10. }
  11. export const EditNotebookDrawer: React.FC<EditNotebookDrawerProps> = ({
  12. isOpen,
  13. onClose,
  14. notebook,
  15. onUpdate,
  16. }) => {
  17. const { t } = useLanguage()
  18. const [name, setName] = useState(notebook.name)
  19. const [description, setDescription] = useState(notebook.description || '')
  20. const [intro, setIntro] = useState(notebook.intro || '')
  21. const [isSubmitting, setIsSubmitting] = useState(false)
  22. // Reset form when notebook changes or drawer opens
  23. useEffect(() => {
  24. if (isOpen) {
  25. setName(notebook.name)
  26. setDescription(notebook.description || '')
  27. setIntro(notebook.intro || '')
  28. }
  29. }, [isOpen, notebook])
  30. const handleSubmit = async (e: React.FormEvent) => {
  31. e.preventDefault()
  32. if (!name.trim()) return
  33. try {
  34. setIsSubmitting(true)
  35. await onUpdate(notebook.id, {
  36. name: name.trim(),
  37. description: description.trim(),
  38. intro: intro.trim(),
  39. })
  40. onClose()
  41. } catch (error) {
  42. console.error('Failed to update notebook:', error)
  43. alert(t('updateFailedRetry'))
  44. } finally {
  45. setIsSubmitting(false)
  46. }
  47. }
  48. return (
  49. <>
  50. {/* Backdrop */}
  51. {isOpen && (
  52. <div
  53. className="fixed inset-0 bg-black/20 backdrop-blur-sm z-40 transition-opacity duration-300"
  54. onClick={onClose}
  55. />
  56. )}
  57. {/* Drawer */}
  58. <div
  59. className={`fixed right-0 top-0 h-full w-full max-w-md bg-white shadow-2xl z-50 transform transition-transform duration-300 ease-out ${isOpen ? 'translate-x-0' : 'translate-x-full'
  60. }`}
  61. >
  62. <div className="flex flex-col h-full">
  63. {/* Header */}
  64. <div className="flex items-center justify-between px-6 py-4 border-b bg-slate-50">
  65. <h2 className="text-xl font-semibold text-slate-800 flex items-center gap-2">
  66. <Save className="w-5 h-5 text-blue-600" />
  67. {t('editNotebookTitle')}
  68. </h2>
  69. <button
  70. onClick={onClose}
  71. className="p-2 text-slate-400 hover:text-slate-600 hover:bg-slate-200 rounded-full transition-colors"
  72. >
  73. <ChevronRight size={24} />
  74. </button>
  75. </div>
  76. {/* Content */}
  77. <div className="flex-1 overflow-y-auto p-6">
  78. <form id="edit-notebook-form" onSubmit={handleSubmit} className="space-y-6">
  79. <div>
  80. <label className="block text-sm font-medium text-slate-700 mb-1">
  81. {t('name')} <span className="text-red-500">*</span>
  82. </label>
  83. <input
  84. type="text"
  85. value={name}
  86. onChange={(e) => setName(e.target.value)}
  87. className="w-full px-4 py-3 border rounded-xl focus:outline-none focus:ring-2 focus:ring-blue-500 bg-slate-50"
  88. placeholder={t('namePlaceholder')}
  89. required
  90. autoFocus
  91. />
  92. </div>
  93. <div>
  94. <label className="block text-sm font-medium text-slate-700 mb-1">
  95. {t('shortDescription')}
  96. </label>
  97. <input
  98. type="text"
  99. value={description}
  100. onChange={(e) => setDescription(e.target.value)}
  101. className="w-full px-4 py-3 border rounded-xl focus:outline-none focus:ring-2 focus:ring-blue-500 bg-slate-50"
  102. placeholder={t('descPlaceholder')}
  103. />
  104. </div>
  105. <div>
  106. <label className="block text-sm font-medium text-slate-700 mb-1">
  107. {t('detailedIntro')}
  108. </label>
  109. <textarea
  110. value={intro}
  111. onChange={(e) => setIntro(e.target.value)}
  112. className="w-full px-4 py-3 border rounded-xl focus:outline-none focus:ring-2 focus:ring-blue-500 bg-slate-50 h-64 resize-none leading-relaxed"
  113. placeholder={t('introPlaceholder')}
  114. />
  115. <p className="mt-1 text-xs text-slate-500">{t('introHelp')}</p>
  116. </div>
  117. </form>
  118. </div>
  119. {/* Footer */}
  120. <div className="p-6 border-t bg-slate-50">
  121. <button
  122. type="submit"
  123. form="edit-notebook-form"
  124. disabled={isSubmitting || !name.trim()}
  125. className="w-full flex items-center justify-center gap-2 px-6 py-3 bg-blue-600 text-white font-medium rounded-xl hover:bg-blue-700 active:scale-[0.98] transition-all disabled:opacity-50 disabled:cursor-not-allowed shadow-lg shadow-blue-600/20"
  126. >
  127. <Save size={20} />
  128. {isSubmitting ? t('saving') : t('save')}
  129. </button>
  130. </div>
  131. </div>
  132. </div>
  133. </>
  134. )
  135. }