Доработка работы с галереей. Работает добавление и изменение порядка изображений. Не работает редактирование.

This commit is contained in:
anibilag 2025-03-25 22:33:35 +03:00
parent 9ba64a453c
commit dc00bca390
4 changed files with 117 additions and 93 deletions

View File

@ -1,91 +1,92 @@
import { useState } from 'react';
import { useState, useEffect } from 'react';
import { GalleryImage } from '../../types';
import { GalleryGrid } from './GalleryGrid';
import { ImageForm } from './ImageForm';
import { EditImageModal } from './EditImageModal';
interface GalleryManagerProps {
images: GalleryImage[];
// setImages: (images: GalleryImage[]) => void; // Добавляем setter для изображений
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) {
const [editingImage, setEditingImage] = useState<GalleryImage | null>(null);
export function GalleryManager({
images,
//setImages,
imageUrl,
setImageUrl,
onAddImage,
onReorder,
onDelete,
onEdit
}: GalleryManagerProps) {
const [newImageUrl, setNewImageUrl] = useState('');
const [newImageCaption, setNewImageCaption] = useState('');
const [newImageAlt, setNewImageAlt] = useState('');
const handleAddImage = () => {
setNewImageUrl(imageUrl);
// Следим за изменением imageUrl и обновляем newImageUrl
useEffect(() => {
if (imageUrl) {
setNewImageUrl(imageUrl);
}
}, [imageUrl]);
const handleAddImage = () => {
if (!newImageUrl.trim()) return;
const newImage: GalleryImage = {
id: Date.now().toString(),
const newImage = {
url: newImageUrl,
caption: newImageCaption,
alt: newImageAlt || newImageCaption
alt: newImageAlt || newImageCaption,
width: 800, // Примерное значение, можно заменить на динамическое
height: 600,
size: 100000, // Примерное значение
format: 'webp', // Формат по умолчанию
};
onChange([...images, newImage]);
onAddImage(newImage); // Вызываем функцию добавления
setNewImageUrl('');
setNewImageCaption('');
setNewImageAlt('');
};
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);
setImageUrl(''); // Очищаем после добавления
};
return (
<div className="space-y-6">
{/* Add New Image */}
<div className="border rounded-lg p-4 space-y-4">
<h3 className="text-lg font-medium text-gray-900">Добавить фото</h3>
<ImageForm
url={newImageUrl}
caption={newImageCaption}
alt={newImageAlt}
onUrlChange={setNewImageUrl}
onCaptionChange={setNewImageCaption}
onAltChange={setNewImageAlt}
onSubmit={handleAddImage}
submitLabel="Добавить в галерею"
/>
</div>
<div className="space-y-6">
<div className="border rounded-lg p-4 space-y-4">
<h3 className="text-lg font-medium text-gray-900">Добавить фото</h3>
<ImageForm
url={newImageUrl}
caption={newImageCaption}
alt={newImageAlt}
onUrlChange={setNewImageUrl}
onCaptionChange={setNewImageCaption}
onAltChange={setNewImageAlt}
onSubmit={handleAddImage}
submitLabel="Добавить в галерею"
/>
</div>
{/* Gallery Preview */}
<div className="border rounded-lg p-4">
<h3 className="text-lg font-medium text-gray-900 mb-4">Изображения галереи</h3>
<GalleryGrid
images={images}
onEdit={setEditingImage}
onDelete={handleRemoveImage}
onReorder={handleReorder}
/>
<div className="border rounded-lg p-4">
<h3 className="text-lg font-medium text-gray-900 mb-4">Изображения галереи</h3>
<GalleryGrid
images={images}
onEdit={(img) => onEdit(img)}
onDelete={(id) => onDelete(id)}
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>
);
}

View File

@ -52,7 +52,7 @@ export function useGallery(articleId: string) {
setImages(images.map(img => img.id === id ? updatedImage : img));
return updatedImage;
} catch (err) {
console.error('Error updating image:', err);
console.error('Ошибка изменения изображения:', err);
throw err;
}
};
@ -62,7 +62,7 @@ export function useGallery(articleId: string) {
await galleryService.deleteImage(id);
setImages(images.filter(img => img.id !== id));
} catch (err) {
console.error('Error deleting image:', err);
console.error('Ошибка удаления изображения:', err);
throw err;
}
};
@ -75,7 +75,7 @@ export function useGallery(articleId: string) {
);
setImages(reorderedImages);
} catch (err) {
console.error('Error reordering images:', err);
console.error('Ошибка реорганизации изображений:', err);
throw err;
}
};

View File

@ -1,23 +1,23 @@
import React, { useState, useEffect, useMemo } from 'react';
import axios from "axios";
import MinutesWord from '../components/MinutesWord';
import { TipTapEditor } from '../components/TipTapEditor';
import { Header } from '../components/Header';
import { GalleryManager } from '../components/GalleryManager';
import { CoverImageUpload } from '../components/ImageUpload/CoverImageUpload';
import { ImageUploader } from '../components/ImageUpload/ImageUploader';
import { useGallery } from '../hooks/useGallery';
import MinutesWord from '../components/MinutesWord';
import { CategoryTitles, CityTitles, CategoryIds, CityIds, Article, Author } from '../types';
import { Pencil, Trash2, ChevronLeft, ChevronRight, ImagePlus, X, ToggleLeft, ToggleRight} from 'lucide-react';
import { CategoryTitles, CityTitles, CategoryIds, CityIds, Article, Author, GalleryImage } from '../types';
import { Pencil, Trash2, ChevronLeft, ChevronRight, ImagePlus, X, ToggleLeft, ToggleRight } from 'lucide-react';
import { useAuthStore } from '../stores/authStore';
const allCategoryIds: number[] = CategoryIds;
const allCityIds: number[] = CityIds;
// Обложка по умоланию для новых статей
const DEFAULT_COVER_IMAGE = '/images/cover-placeholder.webp';
export function AdminPage() {
const { user } = useAuthStore();
const isAdmin = user?.permissions.isAdmin || false;
@ -44,19 +44,36 @@ export function AdminPage() {
const [authors, setAuthors] = useState<Author[]>([]);
const [authorId, setAuthorId] = useState<string>('');
const [newImageUrl, setNewImageUrl] = useState('');
const [displayedImages, setDisplayedImages] = useState<GalleryImage[]>([]);
// Инициализация хука useGallery с текущим значением articleId (editingId)
const {
images: galleryImages,
loading: galleryLoading,
error: galleryError,
// addImage: addGalleryImage,
// updateImage: updateGalleryImage,
// deleteImage: deleteGalleryImage,
addImage: addGalleryImage,
updateImage: updateGalleryImage,
deleteImage: deleteGalleryImage,
reorderImages: reorderGalleryImages,
// refresh: refreshGallery
} = useGallery(editingId || '');
// Синхронизируем displayedImages с galleryImages при изменении galleryImages
/*
useEffect(() => {
setDisplayedImages(galleryImages);
}, [galleryImages]);
*/
// Добавляем useEffect для инициализации displayedImages при изменении editingId
useEffect(() => {
if (editingId) {
setDisplayedImages(galleryImages);
} else {
setDisplayedImages([]); // Очищаем, если editingId сбрасывается
}
}, [editingId, galleryImages]); // Зависимость от galleryImages нужна только для инициализации
// Загрузка списка авторов
useEffect(() => {
const fetchAuthors = async () => {
@ -291,25 +308,6 @@ export function AdminPage() {
// Проверка прав пользователя
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
if (editingId && galleryLoading) {
return (
@ -478,11 +476,29 @@ export function AdminPage() {
)}
</div>
<GalleryManager
images={galleryImages}
images={displayedImages} // Используем displayedImages вместо galleryImages
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));
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>
@ -505,6 +521,8 @@ export function AdminPage() {
setReadTime(5);
setAuthorId(authors[0].id || '');
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"
>

View File

@ -29,6 +29,11 @@ export interface GalleryImage {
url: string;
caption: string;
alt: string;
width: number;
height: number;
size: number;
format: string;
}
export interface Author {