Skip to content

Instantly share code, notes, and snippets.

@muindetuva
Created June 20, 2025 09:01
Show Gist options
  • Select an option

  • Save muindetuva/85c59cc9bc23759951ee36bbf3be808e to your computer and use it in GitHub Desktop.

Select an option

Save muindetuva/85c59cc9bc23759951ee36bbf3be808e to your computer and use it in GitHub Desktop.
// components/MyUploadForm.tsx
'use client';
import React, { useState, useRef, useCallback } from 'react';
import { db, storage } from '../firebase/config'; // Adjust path if needed
import { collection, addDoc, serverTimestamp } from 'firebase/firestore';
import { ref, uploadBytes, getDownloadURL } from 'firebase/storage'; // Import uploadBytes
const MyUploadForm: React.FC = () => {
const [title, setTitle] = useState<string>('');
const [description, setDescription] = useState<string>('');
const [statusMessage, setStatusMessage] = useState<string>('');
const [isSubmitting, setIsSubmitting] = useState<boolean>(false);
const fileInputRef = useRef<HTMLInputElement>(null);
const handleSubmit = useCallback(async (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
setIsSubmitting(true);
setStatusMessage('Submitting...');
// Basic validation for required text fields
if (!title || !description) {
setStatusMessage('Please fill in the title and description.');
setIsSubmitting(false);
return;
}
const imageUrls: string[] = []; // Initialize as empty array
const filesToUpload = fileInputRef.current?.files; // Get the FileList
try {
if (filesToUpload && filesToUpload.length > 0) {
setStatusMessage('Uploading images...');
for (let i = 0; i < filesToUpload.length; i++) {
const file = filesToUpload[i];
const imageFileName = `${Date.now()}-${file.name}`;
const imageRef = ref(storage, `reports/${imageFileName}`);
// --- Use uploadBytes and await its completion ---
const uploadResult = await uploadBytes(imageRef, file);
const url = await getDownloadURL(uploadResult.ref); // Get URL from the result's ref
imageUrls.push(url);
}
setStatusMessage('All images uploaded. Saving data...');
} else {
setStatusMessage('No images selected. Saving data...');
}
// --- Store Data (including image URLs) in Cloud Firestore ---
try {
const docData = {
title,
description,
imageUrls: imageUrls, // Will be empty array if no images uploaded
createdAt: serverTimestamp(),
};
await addDoc(collection(db, "formData"), docData);
console.log("Document written successfully!");
setStatusMessage('Form submitted successfully!');
setTitle('');
setDescription('');
if (fileInputRef.current) {
fileInputRef.current.value = ''; // Clear file input
}
} catch (firestoreError: any) {
console.error("Error writing document to Firestore: ", firestoreError);
setStatusMessage(`Data storage failed: ${firestoreError.message}`);
} finally {
setIsSubmitting(false); // Ensure loading state is reset
}
} catch (overallError: any) {
// This catches errors from image upload loop (e.g., network errors, permission issues)
console.error("An error occurred during form submission:", overallError);
setStatusMessage(`Submission error: ${overallError.message}`);
setIsSubmitting(false);
}
}, [title, description]);
return (
<div className="max-w-md mx-auto p-6 bg-white shadow-md rounded-lg">
<h2 className="text-2xl font-bold mb-4 text-center">Upload Form</h2>
<form onSubmit={handleSubmit} className="space-y-4">
<div>
<label htmlFor="title" className="block text-sm font-medium text-gray-700">Title:</label>
<input
type="text"
id="title"
name="title"
value={title}
onChange={(e) => setTitle(e.target.value)}
required
className="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm"
/>
</div>
<div>
<label htmlFor="description" className="block text-sm font-medium text-gray-700">Description:</label>
<textarea
id="description"
name="description"
value={description}
onChange={(e) => setDescription(e.target.value)}
required
rows={4}
className="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm"
></textarea>
</div>
<div>
<label htmlFor="image" className="block text-sm font-medium text-gray-700">Upload Image (Optional):</label>
<input
type="file"
id="image"
name="image"
accept="image/*"
ref={fileInputRef}
// Add 'multiple' if you want to allow selecting more than one file
className="mt-1 block w-full text-sm text-gray-500 file:mr-4 file:py-2 file:px-4 file:rounded-md file:border-0 file:text-sm file:font-semibold file:bg-indigo-50 file:text-indigo-700 hover:file:bg-indigo-100"
/>
</div>
<button
type="submit"
disabled={isSubmitting}
className="w-full flex justify-center py-2 px-4 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 disabled:opacity-50 disabled:cursor-not-allowed"
>
{isSubmitting ? 'Submitting...' : 'Submit Data'}
</button>
</form>
{statusMessage && (
<div className="mt-4 p-3 rounded-md text-sm text-center"
style={{ backgroundColor: statusMessage.includes('failed') ? '#fee2e2' : (statusMessage.includes('successfully') ? '#d1fae5' : '#e0f2fe'),
color: statusMessage.includes('failed') ? '#991b1b' : (statusMessage.includes('successfully') ? '#065f46' : '#2563eb') }}>
{statusMessage}
</div>
)}
</div>
);
};
export default MyUploadForm;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment