fix: clean up dashboard upload state
- Prevent object URL leaks in image-classification-form by revoking previous preview URL before replacing it, revoking after successful submit, and revoking on component unmount - Improve preview image alt text from generic "Preview" to descriptive Indonesian text "Pratinjau gambar daun jagung untuk diagnosis" - Use auth store setter consistently in dashboard-page logout instead of direct setState call Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.7
parent
1b5c16b135
commit
7fb8b075b8
@@ -1,4 +1,4 @@
|
||||
import { ChangeEvent, FormEvent, useRef, useState } from 'react';
|
||||
import { ChangeEvent, FormEvent, useRef, useState, useEffect } from 'react';
|
||||
import type { DiagnosisRecord } from '@zeavis/shared';
|
||||
import { Upload } from 'lucide-react';
|
||||
import { Button } from '@/components/ui/button';
|
||||
@@ -21,10 +21,25 @@ export function ImageClassificationForm({
|
||||
const [previewUrl, setPreviewUrl] = useState<string | null>(null);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
// Revoke object URL on component unmount
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
if (previewUrl) {
|
||||
URL.revokeObjectURL(previewUrl);
|
||||
}
|
||||
};
|
||||
}, []);
|
||||
|
||||
function handleFileChange(event: ChangeEvent<HTMLInputElement>) {
|
||||
const selectedFile = event.target.files?.[0] ?? null;
|
||||
setError(null);
|
||||
setFile(selectedFile);
|
||||
|
||||
// Revoke previous preview URL before replacing it
|
||||
if (previewUrl) {
|
||||
URL.revokeObjectURL(previewUrl);
|
||||
}
|
||||
|
||||
setPreviewUrl(selectedFile ? URL.createObjectURL(selectedFile) : null);
|
||||
}
|
||||
|
||||
@@ -37,6 +52,12 @@ export function ImageClassificationForm({
|
||||
|
||||
try {
|
||||
await onSubmit(file);
|
||||
|
||||
// Revoke current preview URL after successful submit before setting null
|
||||
if (previewUrl) {
|
||||
URL.revokeObjectURL(previewUrl);
|
||||
}
|
||||
|
||||
setFile(null);
|
||||
setPreviewUrl(null);
|
||||
if (fileInputRef.current) {
|
||||
@@ -80,7 +101,7 @@ export function ImageClassificationForm({
|
||||
</div>
|
||||
|
||||
{previewUrl && (
|
||||
<img src={previewUrl} alt="Preview" className="h-48 rounded-lg object-cover" />
|
||||
<img src={previewUrl} alt="Pratinjau gambar daun jagung untuk diagnosis" className="h-48 rounded-lg object-cover" />
|
||||
)}
|
||||
|
||||
{error && <p className="text-sm text-red-600">{error}</p>}
|
||||
|
||||
@@ -12,6 +12,7 @@ import { apiClient } from '@/lib/api-client';
|
||||
|
||||
export function DashboardPage() {
|
||||
const { user } = useAuthStore();
|
||||
const setUser = useAuthStore((state) => state.setUser);
|
||||
const { dashboardCompact, toggleDashboardCompact } = useUiStore();
|
||||
const queryClient = useQueryClient();
|
||||
const navigate = useNavigate();
|
||||
@@ -62,7 +63,7 @@ export function DashboardPage() {
|
||||
await apiClient.logout();
|
||||
},
|
||||
onSuccess: () => {
|
||||
useAuthStore.setState({ user: null });
|
||||
setUser(null);
|
||||
queryClient.clear();
|
||||
navigate('/login');
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user