PageManagementModal.tsx 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134
  1. import * as React from 'react';
  2. import { GreenPage } from '../types';
  3. import { Icon } from './ui/Icon';
  4. import { useTranslation } from '../hooks/useI18n';
  5. interface PageManagementModalProps {
  6. pages: GreenPage[];
  7. activePageId: string;
  8. onSelectPage: (id: string) => void;
  9. onCreatePage: (name: string) => void;
  10. onUpdatePage: (id: string, newName: string) => void;
  11. onClose: () => void;
  12. }
  13. const PageManagementModal: React.FC<PageManagementModalProps> = ({ pages, activePageId, onSelectPage, onCreatePage, onUpdatePage, onClose }) => {
  14. const { t } = useTranslation();
  15. const [isCreating, setIsCreating] = React.useState(false);
  16. const [newPageName, setNewPageName] = React.useState('');
  17. const [editingPageId, setEditingPageId] = React.useState<string | null>(null);
  18. const [editingPageName, setEditingPageName] = React.useState('');
  19. const handleCreate = () => {
  20. if (newPageName.trim()) {
  21. onCreatePage(newPageName.trim());
  22. setNewPageName('');
  23. setIsCreating(false);
  24. }
  25. };
  26. const handleEdit = (page: GreenPage) => {
  27. setEditingPageId(page.id);
  28. setEditingPageName(page.name);
  29. };
  30. const handleCancelEdit = () => {
  31. setEditingPageId(null);
  32. setEditingPageName('');
  33. };
  34. const handleSaveEdit = () => {
  35. if (editingPageId && editingPageName.trim()) {
  36. onUpdatePage(editingPageId, editingPageName.trim());
  37. handleCancelEdit();
  38. }
  39. };
  40. return (
  41. <div className="fixed inset-0 bg-black/70 z-50 flex items-center justify-center backdrop-blur-sm" onClick={onClose}>
  42. <div className="bg-white dark:bg-gray-800 rounded-2xl shadow-2xl w-full max-w-4xl p-8 border border-gray-200 dark:border-gray-700" onClick={e => e.stopPropagation()}>
  43. <div className="flex justify-between items-center mb-6">
  44. <h2 className="text-3xl font-bold text-gray-900 dark:text-white">{t('modal.pages.title')}</h2>
  45. <button onClick={onClose} className="text-gray-400 dark:text-gray-400 hover:text-gray-900 dark:hover:text-white">
  46. <Icon className="h-8 w-8">
  47. <path strokeLinecap="round" strokeLinejoin="round" d="M6 18L18 6M6 6l12 12" />
  48. </Icon>
  49. </button>
  50. </div>
  51. <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
  52. {pages.map(page => {
  53. if (editingPageId === page.id) {
  54. return (
  55. <div key={page.id} className="p-4 rounded-xl bg-gray-100 dark:bg-gray-700 flex flex-col justify-center gap-3">
  56. <input
  57. type="text"
  58. autoFocus
  59. value={editingPageName}
  60. onChange={e => setEditingPageName(e.target.value)}
  61. onKeyPress={e => e.key === 'Enter' && handleSaveEdit()}
  62. className="w-full bg-white dark:bg-gray-800 p-2 rounded-md border border-gray-300 dark:border-gray-600 text-gray-900 dark:text-white"
  63. />
  64. <div className="flex gap-2">
  65. <button onClick={handleCancelEdit} className="flex-1 bg-gray-300 dark:bg-gray-600 hover:bg-gray-400 dark:hover:bg-gray-500 text-gray-800 dark:text-white font-semibold py-2 rounded-md text-sm">{t('cancel')}</button>
  66. <button onClick={handleSaveEdit} className="flex-1 bg-brand-primary hover:bg-brand-secondary text-white font-semibold py-2 rounded-md text-sm">{t('save')}</button>
  67. </div>
  68. </div>
  69. );
  70. }
  71. return (
  72. <div key={page.id} className={`relative group p-6 rounded-xl text-left bg-gradient-to-br ${page.themeColor} transition-all duration-300 transform hover:scale-105`}>
  73. <button
  74. onClick={() => onSelectPage(page.id)}
  75. className={`w-full h-full text-left focus:outline-none focus:ring-4 focus:ring-white/50 rounded-xl ${page.id === activePageId ? 'ring-4 ring-white' : 'ring-2 ring-transparent'}`}
  76. >
  77. <p className="text-2xl font-bold text-white break-words">{page.name}</p>
  78. <p className="text-white/70 text-sm mt-1">{`greenpage.ai/${page.slug}`}</p>
  79. </button>
  80. {page.id === activePageId && (
  81. <div className="absolute top-4 right-4 bg-white/20 rounded-full h-6 w-6 flex items-center justify-center">
  82. <Icon className="h-4 w-4 text-white">
  83. <path strokeLinecap="round" strokeLinejoin="round" d="M5 13l4 4L19 7" />
  84. </Icon>
  85. </div>
  86. )}
  87. <button onClick={() => handleEdit(page)} title={t('edit')} className="absolute bottom-3 right-3 bg-black/20 text-white/70 hover:bg-black/40 hover:text-white p-1 rounded-full opacity-0 group-hover:opacity-100 transition-opacity">
  88. <Icon className="h-4 w-4"><path d="M15.232 5.232l3.536 3.536m-2.036-5.036a2.5 2.5 0 113.536 3.536L6.5 21.036H3v-3.536L16.732 3.732z" /></Icon>
  89. </button>
  90. </div>
  91. )
  92. })}
  93. {!isCreating ? (
  94. <button
  95. onClick={() => setIsCreating(true)}
  96. className="p-6 rounded-xl flex flex-col items-center justify-center border-2 border-dashed border-gray-300 dark:border-gray-600 hover:border-brand-primary hover:bg-gray-100 dark:hover:bg-gray-700/50 transition-colors"
  97. >
  98. <Icon className="h-12 w-12 text-gray-400 dark:text-gray-500 mb-2"><path d="M12 4v16m8-8H4" /></Icon>
  99. <span className="font-semibold text-gray-500 dark:text-gray-300">{t('modal.pages.create_new')}</span>
  100. </button>
  101. ) : (
  102. <div className="p-6 rounded-xl bg-gray-100 dark:bg-gray-700 flex flex-col justify-center gap-4">
  103. <input
  104. type="text"
  105. autoFocus
  106. value={newPageName}
  107. onChange={e => setNewPageName(e.target.value)}
  108. onKeyPress={e => e.key === 'Enter' && handleCreate()}
  109. placeholder={`${t('name')}...`}
  110. className="w-full bg-white dark:bg-gray-800 p-3 rounded-md border border-gray-300 dark:border-gray-600 text-gray-900 dark:text-white"
  111. />
  112. <div className="flex gap-2">
  113. <button onClick={() => setIsCreating(false)} className="flex-1 bg-gray-300 dark:bg-gray-600 hover:bg-gray-400 dark:hover:bg-gray-500 text-gray-800 dark:text-white font-semibold py-2 rounded-md">{t('cancel')}</button>
  114. <button onClick={handleCreate} className="flex-1 bg-brand-primary hover:bg-brand-secondary text-white font-semibold py-2 rounded-md">{t('add')}</button>
  115. </div>
  116. </div>
  117. )}
  118. </div>
  119. </div>
  120. </div>
  121. );
  122. };
  123. export default PageManagementModal;