December 19, 2025

SnowSOS

A close-up of a hand holding a smartphone displaying the SnowSOS app interface with a large "Request Plow" button. In the background, a blurry yellow pickup truck is actively plowing heavy snow from a suburban driveway during a Minnesota winter storm

To everyone who told me, "Someone needs to make it easier to find a plow guy"... I finally did it! 🚜 After months of tweaking, the SnowSOS prototype is live and ready for testing. No seasonal contracts, no frantic phone calls—just neighbor helping neighbor. Whether you need a pro driver or the "kid next door" to shovel the walk, it's all here. I need my Minnesota friends to help me test this out during the next snowfall. Click the link in my bio to try the prototype! #SnowSOS #MNRealEstate #WinterTech #BetaLaunch

Version 2.0

import React, { useState, useEffect, useCallback } from ‘react’; import { initializeApp } from ‘firebase/app’; import { getAuth, signInAnonymously, signInWithCustomToken, onAuthStateChanged } from ‘firebase/auth’; import { getFirestore, collection, doc, addDoc, updateDoc, onSnapshot, query, where, serverTimestamp, setLogLevel } from ‘firebase/firestore’; // CORRECTED: Icons are imported by their name, not prefixed with ‘Lu’ import { Snowflake, MapPin, User, Truck, Home, ClipboardList, CheckCircle, Loader2, Trash2, AlertTriangle } from ‘lucide-react’; // — Global Variable Definitions — // These variables are provided by the Canvas environment. const appId = typeof __app_id !== ‘undefined’ ? __app_id : ‘default-app-id’; const firebaseConfig = typeof __firebase_config !== ‘undefined’ ? JSON.parse(__firebase_config) : {}; // FIXED: Changed __initialAuthToken to __initial_auth_token to prevent ReferenceError const initialAuthToken = typeof __initial_auth_token !== ‘undefined’ ? __initial_auth_token : null; // Set Firestore log level for debugging (optional) setLogLevel(‘error’); // Set to ‘debug’ if troubleshooting // Initialize Firebase services outside the component to prevent re-initialization let app; let db; let auth; try { app = initializeApp(firebaseConfig); db = getFirestore(app); auth = getAuth(app); } catch (error) { console.error(“Firebase initialization failed:”, error); // Use dummy functions if initialization fails app = null; db = null; auth = null; } // Collection path for public data (shared between all users) const PUBLIC_JOBS_COLLECTION_PATH = `/artifacts/${appId}/public/data/jobs`; /** * Utility function to format the user ID for display. * @param {string} uid The full user ID string. * @returns {string} The shortened, displayable ID. */ const formatUserId = (uid) => uid ? `${uid.substring(0, 4)}…${uid.substring(uid.length – 4)}` : ‘N/A’; const JobCard = ({ job, isOperator, currentUserId, onClaim, onComplete }) => { const isClaimedByMe = job.operatorId === currentUserId; const isMyJob = job.homeownerId === currentUserId; // Define colors and icons based on status let statusClass = ”; let statusText = ”; let statusIcon = null; if (job.status === ‘Open’) { statusClass = ‘bg-green-100 text-green-700 border-green-300’; statusText = ‘Open for Claim’; statusIcon = ; } else if (job.status === ‘Claimed’) { statusClass = ‘bg-yellow-100 text-yellow-700 border-yellow-300’; statusText = isClaimedByMe ? ‘Claimed by Me’ : `Claimed by ${formatUserId(job.claimedByUserId)}`; statusIcon = ; } else if (job.status === ‘Completed’) { statusClass = ‘bg-blue-100 text-blue-700 border-blue-300’; statusText = ‘Completed’; statusIcon = ; } // Determine service type styling – UPDATED to handle ‘Salt’ let typeClass = ”; switch (job.serviceType) { case ‘Plow’: typeClass = ‘bg-indigo-600 text-white’; break; case ‘Shovel’: typeClass = ‘bg-orange-600 text-white’; break; case ‘Salt’: typeClass = ‘bg-teal-600 text-white’; // New color for Salting break; default: typeClass = ‘bg-gray-500 text-white’; break; } // Determine button visibility and text let actionButton = null; if (isOperator) { if (job.status === ‘Open’) { actionButton = ( ); } else if (job.status === ‘Claimed’ && isClaimedByMe) { actionButton = ( ); } } return (
{/* Header: Status and Type */}
{statusIcon} {statusText}
{/* Updated to use typeClass variable */}
{job.serviceType}
{/* Content Details */}

{job.location}

Homeowner: {formatUserId(job.homeownerUserId)} {isMyJob && (You)}

{job.claimedByUserId && (

Operator: {formatUserId(job.claimedByUserId)} {isClaimedByMe && (You)}

)} {job.notes && (

“{job.notes}”

)}
{/* Footer and Action */}

Posted: {job.createdAt ? new Date(job.createdAt.toDate()).toLocaleString() : ‘Loading…’}

{actionButton}
); }; const CreateJobForm = ({ userId, db, onJobCreated }) => { const [location, setLocation] = useState(‘Coon Rapids, MN’); const [serviceType, setServiceType] = useState(‘Plow’); const [notes, setNotes] = useState(”); const [isSaving, setIsSaving] = useState(false); const [error, setError] = useState(”); const handleSubmit = async (e) => { e.preventDefault(); setError(”); if (!userId) { setError(‘Authentication required to create a job.’); return; } if (!location.toLowerCase().includes(‘coon rapids, mn’)) { setError(‘The location must specify “Coon Rapids, MN” to ensure local service.’); return; } setIsSaving(true); try { const jobsRef = collection(db, PUBLIC_JOBS_COLLECTION_PATH); await addDoc(jobsRef, { homeownerId: userId, homeownerUserId: formatUserId(userId), // Storing displayable ID for convenience location, serviceType, notes, status: ‘Open’, operatorId: null, claimedByUserId: null, createdAt: serverTimestamp(), updatedAt: serverTimestamp(), }); // Reset form setLocation(‘Coon Rapids, MN’); setServiceType(‘Plow’); setNotes(”); onJobCreated(); } catch (err) { console.error(“Error creating job: “, err); setError(`Failed to create job. Please check console for details. (Error: ${err.message})`); } finally { setIsSaving(false); } }; return (

Create New Snow Removal Request

{error &&
{error}
} {/* Location Input */}
setLocation(e.target.value)} required className=”mt-1 block w-full rounded-lg border-gray-300 shadow-sm p-3 border focus:border-indigo-500 focus:ring-indigo-500″ placeholder=”e.g., 123 Main St, Coon Rapids, MN” />
{/* Service Type – MODIFIED */}
{/* Notes */}