Files
bDS/src/renderer/components/AISuggestionsModal/AISuggestionsModal.tsx

223 lines
7.8 KiB
TypeScript
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import React, { useState, useEffect, useCallback } from 'react';
import './AISuggestionsModal.css';
export interface AISuggestions {
title?: string;
alt?: string;
caption?: string;
}
export interface CurrentValues {
title: string;
alt: string;
caption: string;
}
interface AISuggestionsModalProps {
isOpen: boolean;
isLoading: boolean;
suggestions: AISuggestions | null;
currentValues: CurrentValues;
error?: string;
onConfirm: (values: Partial<AISuggestions>) => void;
onClose: () => void;
}
export const AISuggestionsModal: React.FC<AISuggestionsModalProps> = ({
isOpen,
isLoading,
suggestions,
currentValues,
error,
onConfirm,
onClose,
}) => {
// Checkbox state - initialized based on whether current values are empty
const [useTitle, setUseTitle] = useState(false);
const [useAlt, setUseAlt] = useState(false);
const [useCaption, setUseCaption] = useState(false);
// Update checkbox state when suggestions arrive, based on whether current fields are empty
useEffect(() => {
if (suggestions) {
setUseTitle(suggestions.title ? !currentValues.title : false);
setUseAlt(suggestions.alt ? !currentValues.alt : false);
setUseCaption(suggestions.caption ? !currentValues.caption : false);
}
}, [suggestions, currentValues]);
const handleBackdropClick = useCallback((e: React.MouseEvent) => {
if (e.target === e.currentTarget && !isLoading) {
onClose();
}
}, [isLoading, onClose]);
const handleConfirm = useCallback(() => {
const valuesToApply: Partial<AISuggestions> = {};
if (useTitle && suggestions?.title) valuesToApply.title = suggestions.title;
if (useAlt && suggestions?.alt) valuesToApply.alt = suggestions.alt;
if (useCaption && suggestions?.caption) valuesToApply.caption = suggestions.caption;
onConfirm(valuesToApply);
}, [useTitle, useAlt, useCaption, suggestions, onConfirm]);
if (!isOpen) return null;
const hasAnySuggestion = suggestions && (suggestions.title || suggestions.alt || suggestions.caption);
const hasAnySelected = useTitle || useAlt || useCaption;
return (
<div className="ai-suggestions-modal-backdrop" onClick={handleBackdropClick}>
<div className="ai-suggestions-modal">
<div className="ai-suggestions-modal-header">
<h2>AI Image Analysis</h2>
{!isLoading && (
<button className="ai-suggestions-modal-close" onClick={onClose} title="Close">
</button>
)}
</div>
<div className="ai-suggestions-modal-body">
{isLoading && (
<div className="ai-suggestions-loading">
<div className="ai-suggestions-spinner"></div>
<p>Analyzing image...</p>
</div>
)}
{error && !isLoading && (
<div className="ai-suggestions-error">
<span className="error-icon"></span>
<span>{error}</span>
</div>
)}
{!isLoading && !error && hasAnySuggestion && (
<div className="ai-suggestions-list">
<p className="ai-suggestions-intro">
Select which AI-generated values to apply. Existing values are preserved by default.
</p>
{suggestions?.title && (
<div className="ai-suggestion-item">
<label className="ai-suggestion-checkbox">
<input
type="checkbox"
checked={useTitle}
onChange={(e) => setUseTitle(e.target.checked)}
/>
<span className="checkmark"></span>
</label>
<div className="ai-suggestion-content">
<div className="ai-suggestion-label">
Title
{currentValues.title && (
<span className="ai-suggestion-has-value" title="This field already has a value">
(has existing value)
</span>
)}
</div>
<div className="ai-suggestion-value">{suggestions.title}</div>
{currentValues.title && (
<div className="ai-suggestion-current">
Current: <em>{currentValues.title}</em>
</div>
)}
</div>
</div>
)}
{suggestions?.alt && (
<div className="ai-suggestion-item">
<label className="ai-suggestion-checkbox">
<input
type="checkbox"
checked={useAlt}
onChange={(e) => setUseAlt(e.target.checked)}
/>
<span className="checkmark"></span>
</label>
<div className="ai-suggestion-content">
<div className="ai-suggestion-label">
Alt Text
{currentValues.alt && (
<span className="ai-suggestion-has-value" title="This field already has a value">
(has existing value)
</span>
)}
</div>
<div className="ai-suggestion-value">{suggestions.alt}</div>
{currentValues.alt && (
<div className="ai-suggestion-current">
Current: <em>{currentValues.alt}</em>
</div>
)}
</div>
</div>
)}
{suggestions?.caption && (
<div className="ai-suggestion-item">
<label className="ai-suggestion-checkbox">
<input
type="checkbox"
checked={useCaption}
onChange={(e) => setUseCaption(e.target.checked)}
/>
<span className="checkmark"></span>
</label>
<div className="ai-suggestion-content">
<div className="ai-suggestion-label">
Caption
{currentValues.caption && (
<span className="ai-suggestion-has-value" title="This field already has a value">
(has existing value)
</span>
)}
</div>
<div className="ai-suggestion-value">{suggestions.caption}</div>
{currentValues.caption && (
<div className="ai-suggestion-current">
Current: <em>{currentValues.caption}</em>
</div>
)}
</div>
</div>
)}
</div>
)}
{!isLoading && !error && !hasAnySuggestion && suggestions && (
<div className="ai-suggestions-empty">
No suggestions were generated for this image.
</div>
)}
</div>
<div className="ai-suggestions-modal-footer">
{isLoading ? (
<button className="button-cancel" disabled>
Please wait...
</button>
) : (
<>
<button className="button-cancel" onClick={onClose}>
Cancel
</button>
{hasAnySuggestion && (
<button
className="button-apply"
onClick={handleConfirm}
disabled={!hasAnySelected}
>
Apply Selected
</button>
)}
</>
)}
</div>
</div>
</div>
);
};