Доработка работы с галереей. Работает добавление и изменение порядка изображений. Не работает редактирование.
This commit is contained in:
parent
9ba64a453c
commit
dc00bca390
@ -1,58 +1,63 @@
|
|||||||
import { useState } from 'react';
|
import { useState, useEffect } from 'react';
|
||||||
import { GalleryImage } from '../../types';
|
import { GalleryImage } from '../../types';
|
||||||
import { GalleryGrid } from './GalleryGrid';
|
import { GalleryGrid } from './GalleryGrid';
|
||||||
import { ImageForm } from './ImageForm';
|
import { ImageForm } from './ImageForm';
|
||||||
import { EditImageModal } from './EditImageModal';
|
|
||||||
|
|
||||||
interface GalleryManagerProps {
|
interface GalleryManagerProps {
|
||||||
images: GalleryImage[];
|
images: GalleryImage[];
|
||||||
|
// setImages: (images: GalleryImage[]) => void; // Добавляем setter для изображений
|
||||||
imageUrl: string;
|
imageUrl: string;
|
||||||
onChange: (images: GalleryImage[]) => void;
|
setImageUrl: (url: string) => void; // Добавил setImageUrl для обновления
|
||||||
|
onAddImage: (imageData: Omit<GalleryImage, 'id'>) => void;
|
||||||
|
onReorder: (images: GalleryImage[]) => void;
|
||||||
|
onDelete: (id: string) => void;
|
||||||
|
onEdit: (image: GalleryImage) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function GalleryManager({ images, imageUrl, onChange }: GalleryManagerProps) {
|
export function GalleryManager({
|
||||||
const [editingImage, setEditingImage] = useState<GalleryImage | null>(null);
|
images,
|
||||||
|
//setImages,
|
||||||
|
imageUrl,
|
||||||
|
setImageUrl,
|
||||||
|
onAddImage,
|
||||||
|
onReorder,
|
||||||
|
onDelete,
|
||||||
|
onEdit
|
||||||
|
}: GalleryManagerProps) {
|
||||||
const [newImageUrl, setNewImageUrl] = useState('');
|
const [newImageUrl, setNewImageUrl] = useState('');
|
||||||
const [newImageCaption, setNewImageCaption] = useState('');
|
const [newImageCaption, setNewImageCaption] = useState('');
|
||||||
const [newImageAlt, setNewImageAlt] = useState('');
|
const [newImageAlt, setNewImageAlt] = useState('');
|
||||||
|
|
||||||
const handleAddImage = () => {
|
// Следим за изменением imageUrl и обновляем newImageUrl
|
||||||
|
useEffect(() => {
|
||||||
|
if (imageUrl) {
|
||||||
setNewImageUrl(imageUrl);
|
setNewImageUrl(imageUrl);
|
||||||
|
}
|
||||||
|
}, [imageUrl]);
|
||||||
|
|
||||||
|
const handleAddImage = () => {
|
||||||
if (!newImageUrl.trim()) return;
|
if (!newImageUrl.trim()) return;
|
||||||
|
|
||||||
const newImage: GalleryImage = {
|
const newImage = {
|
||||||
id: Date.now().toString(),
|
|
||||||
url: newImageUrl,
|
url: newImageUrl,
|
||||||
caption: newImageCaption,
|
caption: newImageCaption,
|
||||||
alt: newImageAlt || newImageCaption
|
alt: newImageAlt || newImageCaption,
|
||||||
|
width: 800, // Примерное значение, можно заменить на динамическое
|
||||||
|
height: 600,
|
||||||
|
size: 100000, // Примерное значение
|
||||||
|
format: 'webp', // Формат по умолчанию
|
||||||
};
|
};
|
||||||
|
|
||||||
onChange([...images, newImage]);
|
onAddImage(newImage); // Вызываем функцию добавления
|
||||||
setNewImageUrl('');
|
setNewImageUrl('');
|
||||||
setNewImageCaption('');
|
setNewImageCaption('');
|
||||||
setNewImageAlt('');
|
setNewImageAlt('');
|
||||||
};
|
setImageUrl(''); // Очищаем после добавления
|
||||||
|
|
||||||
const handleUpdateImage = (updatedImage: GalleryImage) => {
|
|
||||||
onChange(images.map(img => img.id === updatedImage.id ? updatedImage : img));
|
|
||||||
setEditingImage(null);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleRemoveImage = (id: string) => {
|
|
||||||
onChange(images.filter(img => img.id !== id));
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleReorder = (dragIndex: number, dropIndex: number) => {
|
|
||||||
const reorderedImages = [...images];
|
|
||||||
const [draggedImage] = reorderedImages.splice(dragIndex, 1);
|
|
||||||
reorderedImages.splice(dropIndex, 0, draggedImage);
|
|
||||||
onChange(reorderedImages);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="space-y-6">
|
<div className="space-y-6">
|
||||||
{/* Add New Image */}
|
|
||||||
<div className="border rounded-lg p-4 space-y-4">
|
<div className="border rounded-lg p-4 space-y-4">
|
||||||
<h3 className="text-lg font-medium text-gray-900">Добавить фото</h3>
|
<h3 className="text-lg font-medium text-gray-900">Добавить фото</h3>
|
||||||
<ImageForm
|
<ImageForm
|
||||||
@ -67,25 +72,21 @@ export function GalleryManager({ images, imageUrl, onChange }: GalleryManagerPro
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Gallery Preview */}
|
|
||||||
<div className="border rounded-lg p-4">
|
<div className="border rounded-lg p-4">
|
||||||
<h3 className="text-lg font-medium text-gray-900 mb-4">Изображения галереи</h3>
|
<h3 className="text-lg font-medium text-gray-900 mb-4">Изображения галереи</h3>
|
||||||
<GalleryGrid
|
<GalleryGrid
|
||||||
images={images}
|
images={images}
|
||||||
onEdit={setEditingImage}
|
onEdit={(img) => onEdit(img)}
|
||||||
onDelete={handleRemoveImage}
|
onDelete={(id) => onDelete(id)}
|
||||||
onReorder={handleReorder}
|
onReorder={(dragIndex, dropIndex) => {
|
||||||
|
const reorderedImages = [...images];
|
||||||
|
const [draggedImage] = reorderedImages.splice(dragIndex, 1);
|
||||||
|
reorderedImages.splice(dropIndex, 0, draggedImage);
|
||||||
|
onReorder(reorderedImages);
|
||||||
|
// setImages(reorderedImages); // Обновляем состояние изображений
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Edit Image Modal */}
|
|
||||||
{editingImage && (
|
|
||||||
<EditImageModal
|
|
||||||
image={editingImage}
|
|
||||||
onClose={() => setEditingImage(null)}
|
|
||||||
onSave={handleUpdateImage}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
@ -52,7 +52,7 @@ export function useGallery(articleId: string) {
|
|||||||
setImages(images.map(img => img.id === id ? updatedImage : img));
|
setImages(images.map(img => img.id === id ? updatedImage : img));
|
||||||
return updatedImage;
|
return updatedImage;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('Error updating image:', err);
|
console.error('Ошибка изменения изображения:', err);
|
||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -62,7 +62,7 @@ export function useGallery(articleId: string) {
|
|||||||
await galleryService.deleteImage(id);
|
await galleryService.deleteImage(id);
|
||||||
setImages(images.filter(img => img.id !== id));
|
setImages(images.filter(img => img.id !== id));
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('Error deleting image:', err);
|
console.error('Ошибка удаления изображения:', err);
|
||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -75,7 +75,7 @@ export function useGallery(articleId: string) {
|
|||||||
);
|
);
|
||||||
setImages(reorderedImages);
|
setImages(reorderedImages);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('Error reordering images:', err);
|
console.error('Ошибка реорганизации изображений:', err);
|
||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -1,23 +1,23 @@
|
|||||||
import React, { useState, useEffect, useMemo } from 'react';
|
import React, { useState, useEffect, useMemo } from 'react';
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
|
import MinutesWord from '../components/MinutesWord';
|
||||||
import { TipTapEditor } from '../components/TipTapEditor';
|
import { TipTapEditor } from '../components/TipTapEditor';
|
||||||
import { Header } from '../components/Header';
|
import { Header } from '../components/Header';
|
||||||
import { GalleryManager } from '../components/GalleryManager';
|
import { GalleryManager } from '../components/GalleryManager';
|
||||||
import { CoverImageUpload } from '../components/ImageUpload/CoverImageUpload';
|
import { CoverImageUpload } from '../components/ImageUpload/CoverImageUpload';
|
||||||
import { ImageUploader } from '../components/ImageUpload/ImageUploader';
|
import { ImageUploader } from '../components/ImageUpload/ImageUploader';
|
||||||
import { useGallery } from '../hooks/useGallery';
|
import { useGallery } from '../hooks/useGallery';
|
||||||
import MinutesWord from '../components/MinutesWord';
|
import { CategoryTitles, CityTitles, CategoryIds, CityIds, Article, Author, GalleryImage } from '../types';
|
||||||
import { CategoryTitles, CityTitles, CategoryIds, CityIds, Article, Author } from '../types';
|
import { Pencil, Trash2, ChevronLeft, ChevronRight, ImagePlus, X, ToggleLeft, ToggleRight } from 'lucide-react';
|
||||||
import { Pencil, Trash2, ChevronLeft, ChevronRight, ImagePlus, X, ToggleLeft, ToggleRight} from 'lucide-react';
|
|
||||||
import { useAuthStore } from '../stores/authStore';
|
import { useAuthStore } from '../stores/authStore';
|
||||||
|
|
||||||
|
|
||||||
const allCategoryIds: number[] = CategoryIds;
|
const allCategoryIds: number[] = CategoryIds;
|
||||||
const allCityIds: number[] = CityIds;
|
const allCityIds: number[] = CityIds;
|
||||||
|
|
||||||
// Обложка по умоланию для новых статей
|
// Обложка по умоланию для новых статей
|
||||||
const DEFAULT_COVER_IMAGE = '/images/cover-placeholder.webp';
|
const DEFAULT_COVER_IMAGE = '/images/cover-placeholder.webp';
|
||||||
|
|
||||||
|
|
||||||
export function AdminPage() {
|
export function AdminPage() {
|
||||||
const { user } = useAuthStore();
|
const { user } = useAuthStore();
|
||||||
const isAdmin = user?.permissions.isAdmin || false;
|
const isAdmin = user?.permissions.isAdmin || false;
|
||||||
@ -44,19 +44,36 @@ export function AdminPage() {
|
|||||||
const [authors, setAuthors] = useState<Author[]>([]);
|
const [authors, setAuthors] = useState<Author[]>([]);
|
||||||
const [authorId, setAuthorId] = useState<string>('');
|
const [authorId, setAuthorId] = useState<string>('');
|
||||||
const [newImageUrl, setNewImageUrl] = useState('');
|
const [newImageUrl, setNewImageUrl] = useState('');
|
||||||
|
const [displayedImages, setDisplayedImages] = useState<GalleryImage[]>([]);
|
||||||
|
|
||||||
// Инициализация хука useGallery с текущим значением articleId (editingId)
|
// Инициализация хука useGallery с текущим значением articleId (editingId)
|
||||||
const {
|
const {
|
||||||
images: galleryImages,
|
images: galleryImages,
|
||||||
loading: galleryLoading,
|
loading: galleryLoading,
|
||||||
error: galleryError,
|
error: galleryError,
|
||||||
// addImage: addGalleryImage,
|
addImage: addGalleryImage,
|
||||||
// updateImage: updateGalleryImage,
|
updateImage: updateGalleryImage,
|
||||||
// deleteImage: deleteGalleryImage,
|
deleteImage: deleteGalleryImage,
|
||||||
reorderImages: reorderGalleryImages,
|
reorderImages: reorderGalleryImages,
|
||||||
// refresh: refreshGallery
|
// refresh: refreshGallery
|
||||||
} = useGallery(editingId || '');
|
} = useGallery(editingId || '');
|
||||||
|
|
||||||
|
// Синхронизируем displayedImages с galleryImages при изменении galleryImages
|
||||||
|
/*
|
||||||
|
useEffect(() => {
|
||||||
|
setDisplayedImages(galleryImages);
|
||||||
|
}, [galleryImages]);
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Добавляем useEffect для инициализации displayedImages при изменении editingId
|
||||||
|
useEffect(() => {
|
||||||
|
if (editingId) {
|
||||||
|
setDisplayedImages(galleryImages);
|
||||||
|
} else {
|
||||||
|
setDisplayedImages([]); // Очищаем, если editingId сбрасывается
|
||||||
|
}
|
||||||
|
}, [editingId, galleryImages]); // Зависимость от galleryImages нужна только для инициализации
|
||||||
|
|
||||||
// Загрузка списка авторов
|
// Загрузка списка авторов
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const fetchAuthors = async () => {
|
const fetchAuthors = async () => {
|
||||||
@ -291,25 +308,6 @@ export function AdminPage() {
|
|||||||
// Проверка прав пользователя
|
// Проверка прав пользователя
|
||||||
const hasNoPermissions = availableCategoryIds.length === 0 || availableCityIds.length === 0;
|
const hasNoPermissions = availableCategoryIds.length === 0 || availableCityIds.length === 0;
|
||||||
|
|
||||||
/*
|
|
||||||
const handleGalleryImageUpload = async (imageUrl: string) => {
|
|
||||||
try {
|
|
||||||
await addGalleryImage({
|
|
||||||
url: imageUrl,
|
|
||||||
caption: '',
|
|
||||||
alt: '',
|
|
||||||
width: 0,
|
|
||||||
height: 0,
|
|
||||||
size: 0,
|
|
||||||
format: 'webp'
|
|
||||||
});
|
|
||||||
setShowGalleryUploader(false);
|
|
||||||
} catch {
|
|
||||||
setError('Failed to add gallery image');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Show loading state while gallery is loading
|
// Show loading state while gallery is loading
|
||||||
if (editingId && galleryLoading) {
|
if (editingId && galleryLoading) {
|
||||||
return (
|
return (
|
||||||
@ -478,11 +476,29 @@ export function AdminPage() {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<GalleryManager
|
<GalleryManager
|
||||||
images={galleryImages}
|
images={displayedImages} // Используем displayedImages вместо galleryImages
|
||||||
imageUrl={newImageUrl}
|
imageUrl={newImageUrl}
|
||||||
onChange={(images) => {
|
setImageUrl={setNewImageUrl}
|
||||||
// Обработка реорганизации галереи через хук
|
|
||||||
|
onAddImage={async (imageData) => {
|
||||||
|
const newImage = await addGalleryImage(imageData);
|
||||||
|
setNewImageUrl('');
|
||||||
|
setDisplayedImages((prev) => [...prev, newImage]);
|
||||||
|
}}
|
||||||
|
|
||||||
|
onReorder={(images) => {
|
||||||
reorderGalleryImages(images.map(img => img.id));
|
reorderGalleryImages(images.map(img => img.id));
|
||||||
|
setDisplayedImages(images);
|
||||||
|
}}
|
||||||
|
|
||||||
|
onDelete={(id) => {
|
||||||
|
deleteGalleryImage(id);
|
||||||
|
setDisplayedImages((prev) => prev.filter(img => img.id !== id));
|
||||||
|
}}
|
||||||
|
|
||||||
|
onEdit={(image) => {
|
||||||
|
updateGalleryImage(image.id, image);
|
||||||
|
setDisplayedImages((prev) => prev.map(img => (img.id === image.id ? image : img)));
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@ -505,6 +521,8 @@ export function AdminPage() {
|
|||||||
setReadTime(5);
|
setReadTime(5);
|
||||||
setAuthorId(authors[0].id || '');
|
setAuthorId(authors[0].id || '');
|
||||||
setContent('');
|
setContent('');
|
||||||
|
setDisplayedImages([]); // Очищаем отображаемые изображения
|
||||||
|
setNewImageUrl(''); // Очищаем URL нового изображения
|
||||||
}}
|
}}
|
||||||
className="px-4 py-2 border border-gray-300 rounded-md shadow-sm text-sm font-medium text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500"
|
className="px-4 py-2 border border-gray-300 rounded-md shadow-sm text-sm font-medium text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500"
|
||||||
>
|
>
|
||||||
|
@ -29,6 +29,11 @@ export interface GalleryImage {
|
|||||||
url: string;
|
url: string;
|
||||||
caption: string;
|
caption: string;
|
||||||
alt: string;
|
alt: string;
|
||||||
|
width: number;
|
||||||
|
height: number;
|
||||||
|
size: number;
|
||||||
|
format: string;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Author {
|
export interface Author {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user