The Big Sur Land Trust (BSLT) Fire Recovery Monitoring application is a specialized web platform designed to track environmental recovery following wildfires in the Big Sur region. This admin interface allows BSLT staff to manage, organize, and visualize photographs submitted by community members visiting various recovery sites.
The application integrates directly with Google services (Drive and Sheets) to store photos and metadata, providing a centralized dashboard where administrators can:
- View all submitted photos with associated metadata
- Filter and sort images by location, time, and other criteria
- Flag inappropriate content
- Generate time-lapse videos to visualize recovery over time
This project serves as a vital tool for the BSLT's conservation efforts, enabling better data management and visualization for monitoring environmental change in fire-affected areas.
-
Next.js - React framework for server-rendered applications
-
TypeScript - Typed JavaScript for improved code quality
-
Mantine UI - Component library for consistent UI elements
-
Google APIs - Integration with Google services
-
NextAuth.js - Authentication for Next.js
- Yarn - Fast, reliable dependency management
π bslt-fire-recovery/
βββ π components/ # React UI components organized by feature
β βββ π GoogleAPIs/ # Google services integration
β β βββ π FolderSelector.tsx # UI for Google Drive folder selection via Picker API
β β βββ π GoogleAPI.tsx # Core API functions for Drive and Sheets operations
β β βββ π GoogleSignInButton.tsx # OAuth login button with session display
β βββ π PhotoGrid/ # Main photo management UI components
β β βββ π PhotoGrid.tsx # Primary grid display with sorting and filtering logic
β β βββ π PhotoItem.tsx # Individual photo card with metadata and action buttons
β β βββ π Sidebar.tsx # Controls for filtering, sorting, and timelapse generation
β βββ π IndexComponent.tsx # Main app wrapper that orchestrates component rendering
βββ π pages/ # Next.js routing and page components
β βββ π api/ # Backend API routes handled by Next.js
β β βββ π auth/ # Authentication API endpoints
β β β βββ π [...nextauth].ts # NextAuth configuration with Google provider setup
β β β βββ π google.ts # Google-specific authentication handler
β βββ π _app.tsx # Global app wrapper with providers and styles
β βββ π _document.tsx # HTML document customization with Google API scripts
β βββ π index.tsx # Landing page that redirects to photo grid
β βββ π photoGrid.tsx # Page component that renders the PhotoGrid
βββ π public/ # Static assets and client-accessible files
β βββ π static/ # Non-image static resources
β β βββ π DataContext/ # Global state management
β β βββ π DataContext.tsx # Context provider with photos, filters, and UI state
β β βββ π DataContextTypes.tsx # TypeScript interfaces for context data
β βββ π utils/ # Utility functions accessible client-side
β βββ π googleDrive.ts # Helper functions for Google Drive operations
βββ π src/ # Core application source code
β βββ π hooks/ # Custom React hooks
β β βββ π useGooglePicker.ts # Hook for initializing and using Google Picker API
β βββ π types/ # Global TypeScript definitions
β βββ π google-picker.d.ts # Type definitions for Google Picker integration
βββ π .env.local # Environment variables for API keys and secrets
βββ π next.config.mjs # Next.js configuration for images and headers
βββ π package.json # Dependencies and scripts for the project
βββ π tsconfig.json # TypeScript compiler configuration
- PhotoGrid/: Contains the main UI components for displaying and managing photos
PhotoGrid.tsxorchestrates the display of photos with filtering and sortingPhotoItem.tsxrenders individual photo cards with metadata and action buttonsSidebar.tsxprovides controls for filtering, sorting, and timelapse generation
- GoogleAPIs/: Contains components for Google services integration
GoogleAPI.tsxprovides core functions for interacting with Google Drive and SheetsGoogleSignInButton.tsxhandles authentication flow and displays user sessionFolderSelector.tsximplements Google Picker for selecting Drive folders
pages/_app.tsx: Sets up global providers including MantineProvider and SessionProviderpages/api/auth/[...nextauth].ts: Configures NextAuth with Google OAuth and scope permissionspages/photoGrid.tsx: The main page component that displays the photo grid interface
public/static/DataContext/: Contains global state management via React ContextDataContext.tsx: Implements the context provider with all application stateDataContextTypes.tsx: Defines TypeScript interfaces for strongly-typed state
Here are the key functions that are crucial to the application's operation:
- GoogleSignInButton.tsx - Handles Google authentication flow
// Initializes Google Sign-In and manages auth state const initializeGoogleSignIn = () => { ... }
- DataContext.tsx - Provides state management across the application
// Creates context provider with all application state export function DataContextProvider({ children }: DataProviderProps) { ... }
- GoogleAPI.tsx - Core API functions for Google services
// Fetches sheet data from Google Sheets export const fetchSheetData = async (spreadsheetId: string, accessToken: String) => { ... } // Deletes a photo from both Google Drive and the spreadsheet export const deletePhoto = async (...) => { ... } // Extracts the file ID from a Google Drive link export const extractFileId = (url: string) => { ... }
-
Sidebar.tsx - Handles Google Sheets operations
// Fetches and processes sheet data const fetchSheetData = async (spreadsheetId: string) => { ... } // Processes raw sheet data into photo objects const processSheetData = async (data: any) => { ... } // Fetches sheet metadata to get sheet information const fetchSheetMetadata = async (spreadsheetId: string) => { ... }
-
PhotoGrid.tsx - Implements flagging and favoriting functionality
// Updates flag status in Google Sheets const handleFlagPhoto = async (photo: any, index: number) => { ... } // Updates favorite status in Google Sheets const handleFavoritePhotos = async (photo: any, index: number) => { ... }
The application relies on a specific Google Sheet structure with the following columns:
| Column | Name | Description |
|---|---|---|
| A | Timestamp | When the photo was uploaded |
| B | Location | Physical location where the photo was taken |
| C | Uploader Name | Name of the person who uploaded the photo |
| D | Upload Date | Date when the photo was taken |
| E | Upload Time | Time when the photo was taken |
| F | File Link | Google Drive link to the full-resolution photo |
| G | Flagged | Whether the photo has been flagged (Yes or empty) |
| H | Favorites | Whether the photo has been favorited (Yes or empty) |
- PhotoGrid.tsx - Renders the photo grid with filtering and sorting
// Filters photos based on selected criteria const filterPhotos = () => { ... } // Handles flagging inappropriate content const handleFlagPhoto = async (photo: any, index: number) => { ... }
- Sidebar.tsx - Contains core functionality for data fetching and timelapse generation
// Generates timelapse video from selected photos const handleGenerateTimelapse = async (): Promise<void> => { ... } // Fetches metadata about Google Sheet structure const fetchSheetMetadata = async (spreadsheetId: string) => { ... } // Retrieves data from Google Sheets and processes it const fetchSheetData = async (spreadsheetId: string) => { ... } // Processes raw spreadsheet data into structured photo objects const processSheetData = async (data: any) => { ... } // Extracts file ID from Google Drive link formats const extractFileId = (url: string) => { ... } // Fetches thumbnail images for all photos const fetchThumbnails = async (photoData: any[]) => { ... } // Fetches full-resolution image content for timelapse const fetchFileContent = async (fileId: string | null) => { ... }
The Google Sheets integration involves several critical functions that work together:
-
Metadata Retrieval:
fetchSheetMetadatagets structural information about the Google Sheet- Used to identify sheet names and properly target API requests
- Handles fallback methods if sheet names can't be retrieved
-
Data Fetching:
fetchSheetDatamakes API calls to Google Sheets- Retrieves raw photo metadata from the specified spreadsheet
- Handles authentication and error states
-
Data Processing:
processSheetDatatransforms raw spreadsheet values into usable objects- Maps column data to proper field names
- Initializes flagging and favorites states
-
Image Handling:
extractFileIdparses Google Drive links to get file identifiersfetchThumbnailsretrieves preview images for the photo gridfetchFileContentgets full-resolution images for timelapse creation
The timelapse generation process relies on these metadata functions to:
- Locate the correct files in Google Drive
- Organize photos by metadata (location, date, time)
- Apply filters based on user preferences
- Process images in the correct sequence
- Node.js (v14 or higher)
- Yarn package manager
- Google Cloud Platform account
git clone <repository-url>
cd bslt-fire-recoveryyarn install- Go to Google Cloud Console
- Create a new project
- Enable the following APIs:
- Google Drive API
- Google Sheets API
- Google Picker API
- Create OAuth 2.0 credentials:
- Go to APIs & Services > Credentials
- Click Create Credentials > OAuth client ID
- Select Web application
- Add authorized redirect URIs:
http://localhost:3000/api/auth/callback/google(for development)https://your-production-domain.com/api/auth/callback/google(for production)
- Note your Client ID and Client Secret
- Create an API Key:
- Go to APIs & Services > Credentials
- Click Create Credentials > API Key
- Restrict the key to the APIs you're using (recommended)
Create a .env.local file with the following variables:
GOOGLE_CLIENT_ID=your-client-id-from-google-console
GOOGLE_CLIENT_SECRET=your-client-secret-from-google-console
NEXT_PUBLIC_GOOGLE_CLIENT_ID=your-client-id-from-google-console
NEXT_PUBLIC_GOOGLE_API_KEY=your-google-api-key
NEXTAUTH_URL=http://localhost:3000
NEXTAUTH_SECRET=your-random-secret-key-min-32-chars
yarn devNavigate to http://localhost:3000 in your browser
- Sign in using your Google account credentials
- The application will request necessary permissions to access Google Drive and Sheets
- After signing in, click "Import Sheet" button
- Select the Google Sheet containing photo metadata
- Sheet should have the following columns:
- Timestamp
- Location
- Uploader Name
- Upload Date
- Upload Time
- File Link
- Flagged (contains "Yes" or is blank)
- Favorites (contains "Yes" or is blank)
- Sheet should have the following columns:
- Once the sheet is imported, the photo grid will display all images
- Each photo card shows:
- Thumbnail image
- Location information
- Uploader name
- Date and time taken
- Upload timestamp
- Use the sidebar controls to:
- Sort by location, timestamp, or uploader name
- Filter by specific location
- Set date range filters
- Filter by time of day
- Show only flagged photos
The following features are currently working:
- View Photos: Click "View" to open the original high-resolution image
- Flag Photos: Mark inappropriate or noteworthy content
- Flagging updates the Google Sheet with a "Yes" value in the Flagged column
- Flagged photos are visually marked with an icon
- Favorite Photos: Mark important or useful photos
- Favoriting updates the Google Sheet with a "Yes" value in the Favorites column
- Favorited photos are visually marked with a star icon
- Delete Photos: Remove photos from both Google Drive and the spreadsheet
- Requires confirmation to prevent accidental deletion
- Select photos for inclusion in timelapse
- Use individual checkboxes or "Select All" button
- Click "Generate Timelapse" to create a video
- Wait for processing to complete (progress is shown)
- The timelapse will automatically download as a .webm file
The application integrates with a specific Google Sheet format with the following columns:
| Column | Name | Description |
|---|---|---|
| A | Timestamp | When the photo was uploaded (e.g., 2025-01-15T09:30:45Z) |
| B | Location | Physical location where the photo was taken (e.g., Ridge Trail) |
| C | Uploader Name | Name of the person who uploaded the photo (e.g., John Smith) |
| D | Upload Date | Date when the photo was taken (e.g., 04/01/2024) |
| E | Upload Time | Time when the photo was taken (e.g., 9:15:32 AM) |
| F | File Link | Google Drive link to the full-resolution photo |
| G | Flagged | Whether the photo has been flagged (Yes or empty) |
| H | Favorites | Whether the photo has been favorited (Yes or empty) |
The "File Link" column should contain direct Google Drive links to the photos. These links should be in one of the following formats:
https://drive.google.com/file/d/{FILE_ID}/view?usp=sharing- Links containing
id={FILE_ID}parameter
The application automatically extracts the file ID from either format to:
- Fetch thumbnails for the photo grid display
- Retrieve full-resolution images for timelapse generation
- Perform deletion operations when needed
- Administrators can flag photos that may be inappropriate or require special attention
- When a photo is flagged, the application updates column G in the Google Sheet with "Yes"
- Flagged photos are visually marked with an icon in the UI
- Users can filter to view only flagged photos
- Important or particularly useful photos can be marked as favorites
- When a photo is favorited, the application updates column H in the Google Sheet with "Yes"
- Favorited photos are visually marked with a star icon in the UI
- Users can filter to view only favorited photos
- Favorites can be used to curate photos for timelapses or presentations
The application offers multiple ways to filter and sort the photo collection:
-
Sort by:
- Location (A-Z or Z-A)
- Taken time/date (oldest or newest first)
- Uploader name (A-Z or Z-A)
- Flagged status
- Favorites first
-
Filter by:
- Specific location
- Date range
- Time of day range
- Show only flagged photos
- Show only favorited photos
The timelapse generation process follows these steps:
- The application fetches full-resolution images for all selected photos
- Each image is processed into multiple frames (for display duration)
- Frames are combined into a video stream using the browser's MediaRecorder API
- The resulting WebM video file is automatically downloaded to the user's device
- The timelapse generation happens entirely in the browser without server processing
- The process can be memory-intensive for large numbers of high-resolution photos
- Progress indicators show both download progress and processing status
Develop a separate view for public users to:
- View approved photos without editing capabilities
- Filter by location to see recovery progress
- View pre-generated timelapses
Implement one or more of these approaches to enable public contribution:
-
Web Upload Form
Create a simple, mobile-friendly form where visitors can upload photos directly:- Implement basic validation (file type, size, required metadata)
- Add reCAPTCHA to prevent spam
- Store submissions in a "pending" state for admin approval
-
Dedicated Email Submission
Set up a dedicated email address (e.g.,bsltfirerecoveryphotoproject@gmail.com):- Create an automated email processor to extract photos and metadata
- Add email templates for submission confirmation and status updates
-
Location-Based QR Codes
Generate unique QR codes for each monitoring location:- Place codes on trailhead signs with simple instructions
- QR codes open a mobile-optimized submission form pre-filled with location data
- Include photo examples of what to capture for consistency
-
Custom Mobile Application
Develop a simple BSLT-branded mobile app for iOS and Android:- Add camera integration with location tagging
- Provide educational content about fire recovery monitoring
-
iNaturalist Integration
Partner with iNaturalist:- Create a dedicated BSLT Fire Recovery project
- Develop an API integration to pull relevant observations automatically API Docs
- Add guidelines for proper tagging and documentation
- Create new pages in the
pagesdirectory for public routes - Implement read-only versions of components
- Add submission handlers and validation
- Create a moderation queue for new submissions
Implement role-based access control:
- Admin: Full access to all features
- Moderator: Can flag but not delete photos
- Viewer: Read-only access to approved content
- Extend
NextAuth.jsconfiguration to include roles - Add role information to user sessions
- Create middleware to check permissions
Implement comprehensive testing:
- Unit Tests: For utility functions
- Component Tests: For UI elements
- Integration Tests: For API routes
- End-to-End Tests: For critical workflows
- Utilize Jest and React Testing Library
- Create test files in a
__tests__directory - Add testing scripts to
package.json
Since the previous cohort is unreachable:
- Create a new GitHub repository
- Transfer all code with proper documentation
- Set up CI/CD pipelines
- Vercel (recommended for Next.js)
- Netlify
- AWS Amplify
- Enhance error handling and recovery mechanisms
- Optimize performance for large photo collections
- Add statistics and reporting features
- Implement image processing for better timelapse quality
- Always create a new branch for your work:
git checkout -b feature/your-feature-name
- Use descriptive branch names with prefixes:
feature/for new featuresfix/for bug fixesdocs/for documentation updatesrefactor/for code refactoring
- Document your findings and implementation details:
- Update the README when adding significant features
- Add inline code comments for complex logic
- Create or update documentation files in a
docsdirectory
- List working and non-working code:
- Add
TODOcomments for incomplete features - Use
FIXMEcomments for known issues - Add comments explaining workarounds or limitations
- Add
- Write clear, descriptive commit messages:
feat: Add photo flagging functionality - Implement flag button in PhotoItem component - Add Google Sheets API integration to update flagged status - Update UI to show flag icon on flagged photos
- Create detailed pull requests with:
- Clear description of changes
- Screenshots if applicable
- Notes for the next cohort
- Testing steps
This project is maintained by CSUMB service learning students. Please coordinate with the current project manager before making contributions.
- Salvatore Eze - CSUMB Service Learning Student (Spring 2025) - ezesalvatore4@gmail.com
- Logan Druley- CSUMB Service Learning Student (Spring 2025) - ldruley@csumb.edu
- Shaun Rose - shaunrose831@gmail.com | 831-710-8120
- Noel Hann
For questions or coordination, please contact:
- Jenny Jacox (BSLT Project Stakeholder)