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:
Asep Haryana Saputra
2026-05-22 17:07:18 +00:00
co-authored by Claude Opus 4.7
parent 1b5c16b135
commit 7fb8b075b8
2 changed files with 25 additions and 3 deletions
@@ -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>}
+2 -1
View File
@@ -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');
},