iamge modal
This commit is contained in:
@@ -52,6 +52,7 @@ export const createImagesPageConfig = (
|
||||
setCurrentPage: (page: number) => void;
|
||||
maxInArticleImages?: number; // Optional: max in-article images to display
|
||||
onGenerateImages?: (contentId: number) => void; // Handler for generate images button
|
||||
onImageClick?: (contentId: number, imageType: 'featured' | 'in_article', position?: number) => void; // Handler for image click
|
||||
}
|
||||
): ImagesPageConfig => {
|
||||
const maxImages = handlers.maxInArticleImages || 5; // Default to 5 in-article images
|
||||
@@ -84,7 +85,10 @@ export const createImagesPageConfig = (
|
||||
sortable: false,
|
||||
width: '200px',
|
||||
render: (_value: any, row: ContentImagesGroup) => (
|
||||
<ContentImageCell image={row.featured_image} />
|
||||
<ContentImageCell
|
||||
image={row.featured_image}
|
||||
onImageClick={handlers.onImageClick ? () => handlers.onImageClick!(row.content_id, 'featured') : undefined}
|
||||
/>
|
||||
),
|
||||
},
|
||||
];
|
||||
@@ -98,7 +102,12 @@ export const createImagesPageConfig = (
|
||||
width: '200px',
|
||||
render: (_value: any, row: ContentImagesGroup) => {
|
||||
const image = row.in_article_images.find(img => img.position === i);
|
||||
return <ContentImageCell image={image || null} />;
|
||||
return (
|
||||
<ContentImageCell
|
||||
image={image || null}
|
||||
onImageClick={handlers.onImageClick && image ? () => handlers.onImageClick!(row.content_id, 'in_article', i) : undefined}
|
||||
/>
|
||||
);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ import {
|
||||
fetchImageGenerationSettings,
|
||||
generateImages,
|
||||
bulkUpdateImagesStatus,
|
||||
ContentImage,
|
||||
} from '../../services/api';
|
||||
import { useToast } from '../../components/ui/toast/ToastContainer';
|
||||
import { FileIcon, DownloadIcon, BoltIcon } from '../../icons';
|
||||
@@ -20,6 +21,7 @@ import ImageQueueModal, { ImageQueueItem } from '../../components/common/ImageQu
|
||||
import SingleRecordStatusUpdateModal from '../../components/common/SingleRecordStatusUpdateModal';
|
||||
import { useResourceDebug } from '../../hooks/useResourceDebug';
|
||||
import PageHeader from '../../components/common/PageHeader';
|
||||
import { Modal } from '../../components/ui/modal';
|
||||
|
||||
export default function Images() {
|
||||
const toast = useToast();
|
||||
@@ -85,6 +87,10 @@ export default function Images() {
|
||||
const [statusUpdateRecordName, setStatusUpdateRecordName] = useState<string>('');
|
||||
const [isUpdatingStatus, setIsUpdatingStatus] = useState(false);
|
||||
|
||||
// Image modal state
|
||||
const [isImageModalOpen, setIsImageModalOpen] = useState(false);
|
||||
const [modalImageUrl, setModalImageUrl] = useState<string | null>(null);
|
||||
|
||||
// Load images - wrapped in useCallback
|
||||
const loadImages = useCallback(async () => {
|
||||
setLoading(true);
|
||||
@@ -353,6 +359,58 @@ export default function Images() {
|
||||
}
|
||||
}, [toast, images, buildImageQueue]);
|
||||
|
||||
// Helper function to convert image_path to web-accessible URL
|
||||
const getImageUrl = useCallback((image: ContentImage | null): string | null => {
|
||||
if (!image || !image.image_path) return null;
|
||||
|
||||
// Check if image_path is a valid local file path (not a URL)
|
||||
const isValidLocalPath = (imagePath: string): boolean => {
|
||||
if (imagePath.startsWith('http://') || imagePath.startsWith('https://')) {
|
||||
return false;
|
||||
}
|
||||
return imagePath.includes('ai-images');
|
||||
};
|
||||
|
||||
if (!isValidLocalPath(image.image_path)) return null;
|
||||
|
||||
// Convert local file path to web-accessible URL
|
||||
if (image.image_path.includes('ai-images')) {
|
||||
const filename = image.image_path.split('ai-images/')[1] || image.image_path.split('ai-images\\')[1];
|
||||
if (filename) {
|
||||
return `/images/ai-images/${filename}`;
|
||||
}
|
||||
}
|
||||
|
||||
if (image.image_path.startsWith('/images/')) {
|
||||
return image.image_path;
|
||||
}
|
||||
|
||||
const filename = image.image_path.split('/').pop() || image.image_path.split('\\').pop();
|
||||
return filename ? `/images/ai-images/${filename}` : null;
|
||||
}, []);
|
||||
|
||||
// Handle image click - open modal with single image
|
||||
const handleImageClick = useCallback((contentId: number, imageType: 'featured' | 'in_article', position?: number) => {
|
||||
const contentGroup = images.find(g => g.content_id === contentId);
|
||||
if (!contentGroup) return;
|
||||
|
||||
let image: ContentImage | null = null;
|
||||
|
||||
if (imageType === 'featured' && contentGroup.featured_image) {
|
||||
image = contentGroup.featured_image;
|
||||
} else if (imageType === 'in_article' && position) {
|
||||
image = contentGroup.in_article_images.find(img => img.position === position) || null;
|
||||
}
|
||||
|
||||
if (image && image.status === 'generated') {
|
||||
const url = getImageUrl(image);
|
||||
if (url) {
|
||||
setModalImageUrl(url);
|
||||
setIsImageModalOpen(true);
|
||||
}
|
||||
}
|
||||
}, [images, getImageUrl]);
|
||||
|
||||
// Get max in-article images from the data (to determine column count)
|
||||
const maxInArticleImages = useMemo(() => {
|
||||
if (images.length === 0) return 5; // Default
|
||||
@@ -370,8 +428,9 @@ export default function Images() {
|
||||
setCurrentPage,
|
||||
maxInArticleImages,
|
||||
onGenerateImages: handleGenerateImages,
|
||||
onImageClick: handleImageClick,
|
||||
});
|
||||
}, [searchTerm, statusFilter, maxInArticleImages, handleGenerateImages]);
|
||||
}, [searchTerm, statusFilter, maxInArticleImages, handleGenerateImages, handleImageClick]);
|
||||
|
||||
// Calculate header metrics
|
||||
const headerMetrics = useMemo(() => {
|
||||
@@ -479,6 +538,27 @@ export default function Images() {
|
||||
isLoading={isUpdatingStatus}
|
||||
/>
|
||||
|
||||
{/* Image Modal - 800px wide, auto height */}
|
||||
<Modal
|
||||
isOpen={isImageModalOpen}
|
||||
onClose={() => {
|
||||
setIsImageModalOpen(false);
|
||||
setModalImageUrl(null);
|
||||
}}
|
||||
className="max-w-[800px] w-full mx-4"
|
||||
>
|
||||
{modalImageUrl && (
|
||||
<div className="p-6">
|
||||
<img
|
||||
src={modalImageUrl}
|
||||
alt="Content image"
|
||||
className="w-full h-auto object-contain rounded-lg"
|
||||
style={{ maxHeight: '90vh' }}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</Modal>
|
||||
|
||||
{/* AI Function Logs - Display below table (only when Resource Debug is enabled) */}
|
||||
{resourceDebugEnabled && aiLogs.length > 0 && (
|
||||
<div className="mt-6 bg-gray-50 dark:bg-gray-800 rounded-lg border border-gray-200 dark:border-gray-700 p-4">
|
||||
|
||||
Reference in New Issue
Block a user