feat: Initialize a React application with Vite for image analysis and translation, configured with Docker and Gitea CI/CD.
All checks were successful
Docker Build and Push / build-and-push (push) Successful in 51s

This commit is contained in:
almazlar
2026-02-08 23:36:40 +03:00
parent 3ed4a4e985
commit 282b5f4d19
20 changed files with 3883 additions and 338 deletions

160
src/App.jsx Normal file
View File

@@ -0,0 +1,160 @@
import { useState } from 'react';
import { Camera, RefreshCw, Languages, Loader2 } from 'lucide-react';
import { resizeImage } from './utils/imageUtils';
import { analyzeImage, translateText } from './services/api';
function App() {
const [image, setImage] = useState(null); // { base64, previewUser }
const [status, setStatus] = useState('idle'); // idle, analyzing, success, translating, translated, error
const [description, setDescription] = useState('');
const [translation, setTranslation] = useState('');
const [error, setError] = useState('');
const handleFileChange = async (e) => {
if (e.target.files && e.target.files[0]) {
try {
const file = e.target.files[0];
const resized = await resizeImage(file);
setImage(resized);
setStatus('ready');
setError('');
setDescription('');
setTranslation('');
} catch (err) {
console.error(err);
setError("Görsel işlenirken bir hata oluştu.");
}
}
};
const handleAnalyze = async () => {
if (!image) return;
setStatus('analyzing');
setError('');
try {
const result = await analyzeImage(image.base64);
setDescription(result);
setStatus('success');
} catch (err) {
setError("Analiz sırasında hata oluştu: " + err.message);
setStatus('error');
}
};
const handleTranslate = async () => {
if (!description) return;
setStatus('translating');
try {
const result = await translateText(description);
setTranslation(result);
setStatus('translated');
} catch (err) {
setError("Çeviri sırasında hata oluştu: " + err.message);
// Keep status as success to show description still
setStatus('success');
}
};
const handleReset = () => {
setImage(null);
setStatus('idle');
setDescription('');
setTranslation('');
setError('');
};
return (
<div className="container">
<h2>Bu resimde ne var?</h2>
<div className="upload-section" onClick={() => document.getElementById('fileInput').click()}>
<input
type="file"
id="fileInput"
hidden
accept="image/*"
onChange={handleFileChange}
/>
{image ? (
<p>{image.previewUser ? "Görsel Seçildi" : "Görsel Yükleniyor..."}</p>
) : (
<p style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', gap: '8px' }}>
<Camera className="icon" /> 📷 Görseli seçmek için buraya tıklayın
</p>
)}
</div>
{image && (
<img
src={image.previewUser}
alt="Preview"
className="preview-image"
/>
)}
{error && (
<div style={{ color: '#ff6b6b', marginTop: '10px', textAlign: 'center' }}>
{error}
</div>
)}
<button
onClick={handleAnalyze}
disabled={!image || status === 'analyzing' || status === 'translating' || (status !== 'ready' && status !== 'error')}
style={{ display: (status === 'idle' || status === 'ready' || status === 'analyzing' || status === 'error') ? 'flex' : 'none' }}
>
{status === 'analyzing' ? (
<>
<Loader2 className="loader" /> Düşünüyor...
</>
) : (
"Analiz Et"
)}
</button>
{(status === 'success' || status === 'translating' || status === 'translated') && (
<div className="result-box">
<span className="label">English Description</span>
<div>{description}</div>
{status !== 'translated' && (
<button
className="translate-btn"
onClick={handleTranslate}
disabled={status === 'translating'}
>
{status === 'translating' ? (
<>
<Loader2 className="loader" /> Çevriliyor...
</>
) : (
<>
<Languages className="icon" /> Türkçeye Çevir
</>
)}
</button>
)}
</div>
)}
{(status === 'translated' || translation) && (
<div className="result-box translation">
<span className="label">Türkçe Çeviri</span>
<div>{translation}</div>
</div>
)}
{status !== 'idle' && (
<button className="reset-btn" onClick={handleReset}>
<RefreshCw className="icon" /> Yeni Analiz Başlat
</button>
)}
</div>
);
}
export default App;