Skip to content

Latest commit

 

History

History
342 lines (281 loc) · 14.1 KB

File metadata and controls

342 lines (281 loc) · 14.1 KB

Collaborative Spreadsheet

A lightweight, real-time collaborative spreadsheet application built with Next.js, TypeScript, Tailwind CSS, and Firebase. This project demonstrates real-time collaboration capabilities similar to Google Sheets, with a focus on clean architecture and modern web technologies.

🔗 Links

📋 Project Overview

This project is a real-time collaborative spreadsheet editor that allows multiple users to work on the same document simultaneously. Built as part of the Trademarkia Frontend Engineering Assignment, it showcases:

  • Real-time data synchronization across multiple users
  • Formula evaluation engine with cell references
  • Modern UI/UX with a focus on usability
  • Clean architecture with TypeScript and Next.js App Router
  • Firebase integration for authentication and real-time database

🛠️ Tech Stack

  • Framework: Next.js 15 (App Router)
  • Language: TypeScript (Strict mode)
  • Styling: Tailwind CSS
  • Database: Firebase Firestore
  • Authentication: Firebase Authentication
  • State Management: Zustand
  • Deployment: Vercel

✨ Features Implemented

Core Features

Real-time Collaborative Editing - Multiple users can edit the same spreadsheet simultaneously
Spreadsheet Grid - 50 rows × 26 columns with numbered rows and lettered columns
Formula Support - Implements =SUM(A1:A5) and basic arithmetic operations (+, -, *, /)
Presence Indicators - See active users with color-coded avatars
Write-state Indicator - Visual feedback showing "Syncing..." or "All changes saved"
Authentication - Google Sign-In and Guest mode with display names
Document Dashboard - List all spreadsheets with title, last modified, and creator info

Bonus Features

Cell Formatting - Bold, italic, and text color formatting per cell
Export Support - Export spreadsheets to CSV and JSON formats
Keyboard Navigation - Arrow keys, Tab, Shift+Tab, Enter, Shift+Enter for seamless navigation
Column/Row Resize - Drag to resize columns and rows with minimum constraints
Column/Row Reorder - Drag-and-drop to reorder columns and rows

UI/UX Enhancements

  • Modern SaaS-style dashboard with interactive cards
  • Smooth hover effects and transitions
  • Purple gradient theme throughout
  • Editable document titles with placeholder behavior
  • Professional formatting toolbar
  • Empty states and loading indicators
  • Responsive design

🏗️ Architecture

High-Level Architecture

┌─────────────────────────────────────────────────────────────┐
│                         Next.js App                          │
│  ┌────────────────────────────────────────────────────────┐ │
│  │              React Components (UI Layer)                │ │
│  │  • Dashboard  • Spreadsheet  • Toolbar  • Cells        │ │
│  └────────────────────────────────────────────────────────┘ │
│                            ↕                                 │
│  ┌────────────────────────────────────────────────────────┐ │
│  │           State Management (Zustand)                    │ │
│  │  • Auth State  • Sync Status  • Local Edit State       │ │
│  └────────────────────────────────────────────────────────┘ │
│                            ↕                                 │
│  ┌────────────────────────────────────────────────────────┐ │
│  │              Firebase SDK (Client)                      │ │
│  │  • Firestore Listeners  • Auth Methods                 │ │
│  └────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
                             ↕
┌─────────────────────────────────────────────────────────────┐
│                    Firebase Backend                          │
│  ┌──────────────────────┐  ┌──────────────────────────────┐│
│  │   Firestore DB       │  │   Firebase Auth              ││
│  │  • documents/        │  │  • Google Provider           ││
│  │  • presence/         │  │  • Anonymous Provider        ││
│  └──────────────────────┘  └──────────────────────────────┘│
└─────────────────────────────────────────────────────────────┘

Key Architectural Decisions

Next.js App Router

  • Server components for better performance
  • File-system based routing
  • Built-in API routes (not used, Firebase handles backend)

Firebase Firestore

  • Real-time listeners with onSnapshot for live updates
  • Document-based structure for spreadsheets
  • Separate collection for presence tracking

State Management Strategy

  • Local State: React useState for component-specific state (edit mode, input values)
  • Client State: Zustand for auth and sync status (lightweight, no boilerplate)
  • Server State: Firestore for persistent data (real-time by default)

Spreadsheet Grid Rendering

  • React components for each cell
  • React.memo to prevent unnecessary re-renders
  • Local edit state to avoid re-render issues during typing
  • Controlled inputs with debounced Firebase writes (500ms)

Formula Evaluation

  • Custom parser supporting cell references (A1, B2) and ranges (A1:A5)
  • Safe evaluation using Function constructor with controlled scope
  • Single-pass evaluation to prevent circular references

🔄 Real-Time Collaboration

The application uses Firebase Firestore's real-time listeners to enable collaborative editing:

  1. Document Sync: Each spreadsheet is stored as a Firestore document with a cells object containing all cell data
  2. Real-time Listeners: onSnapshot listeners detect changes and update the UI immediately
  3. Optimistic Updates: UI updates instantly while changes are being saved to Firebase
  4. Debounced Writes: Cell updates are debounced (500ms) to reduce database operations
  5. Presence System: Separate Firestore collection tracks active users, updated every 5 seconds
  6. Conflict Resolution: Last-write-wins strategy (simple and predictable)

Data Model

// Document structure in Firestore
{
  id: string
  title: string
  lastModified: number
  createdBy: string
  cells: {
    "A1": { value: "Hello", bold: true },
    "B2": { formula: "=SUM(A1:A5)" },
    "C3": { value: "123", color: "#EF4444" }
  }
  columnWidths: { "A": 120, "B": 150 }
  rowHeights: { 1: 40, 2: 50 }
  columnOrder: ["A", "B", "C", ...]
  rowOrder: [1, 2, 3, ...]
}

// Presence structure
{
  [userId]: {
    id: string
    name: string
    email?: string
    color: string
    lastSeen: number
  }
}

📁 Project Structure

collaborative-spreadsheet/
├── app/
│   ├── document/[id]/
│   │   └── page.tsx              # Spreadsheet editor page
│   ├── layout.tsx                # Root layout with metadata
│   ├── page.tsx                  # Dashboard with document list
│   ├── globals.css               # Global styles
│   └── favicon.ico
│
├── components/
│   ├── dashboard/
│   │   ├── DeleteSpreadsheetModal.tsx
│   │   └── DocumentActionsMenu.tsx
│   ├── editor/
│   │   └── ExportMenu.tsx
│   ├── layout/
│   │   ├── Navbar.tsx            # Top navigation bar
│   │   ├── PageContainer.tsx     # Page wrapper component
│   │   └── UserDropdown.tsx      # User menu dropdown
│   ├── spreadsheet/
│   │   └── FormattingToolbar.tsx # Cell formatting controls
│   ├── ui/
│   │   ├── Avatar.tsx            # User avatar component
│   │   ├── Badge.tsx             # Badge component
│   │   ├── Button.tsx            # Reusable button component
│   │   ├── Card.tsx              # Card component
│   │   ├── Dropdown.tsx          # Dropdown component
│   │   ├── Input.tsx             # Input component
│   │   └── Modal.tsx             # Modal component
│   ├── AuthModal.tsx             # Authentication UI
│   ├── DocumentCard.tsx          # Document list item
│   ├── FormulaBar.tsx            # Formula input bar
│   ├── PresenceIndicator.tsx     # Active users display
│   ├── SaveStatus.tsx            # Sync status indicator
│   ├── Spreadsheet.tsx           # Main spreadsheet component
│   ├── SpreadsheetCell.tsx       # Individual cell component
│   └── Toolbar.tsx               # Editor toolbar
│
├── lib/
│   ├── colors.ts                 # User color palette
│   ├── export.ts                 # CSV/JSON export utilities
│   ├── firebase.ts               # Firebase initialization
│   ├── formula-parser.ts         # Formula evaluation engine
│   ├── store.ts                  # Zustand stores
│   └── types.ts                  # TypeScript interfaces
│
├── public/                       # Static assets
├── .env.local                    # Environment variables (not in git)
├── .gitignore
├── next.config.ts
├── package.json
├── tsconfig.json
└── README.md

Key Components

  • app/page.tsx: Dashboard listing all documents
  • app/document/[id]/page.tsx: Spreadsheet editor with dynamic routing
  • components/Spreadsheet.tsx: Main spreadsheet logic, handles real-time sync, formulas, resize, reorder
  • components/SpreadsheetCell.tsx: Individual cell with edit/display modes, formatting, keyboard navigation
  • lib/formula-parser.ts: Formula evaluation engine supporting SUM and arithmetic
  • lib/firebase.ts: Firebase configuration and initialization
  • lib/store.ts: Zustand stores for auth and sync status

🚀 Running Locally

Prerequisites

  • Node.js 18+ and npm
  • Firebase account
  • Git

Setup Instructions

  1. Clone the repository
git clone https://github.com/Monkdev7/trademarkia-collaborative-spreadsheet.git
cd collaborative-spreadsheet
  1. Install dependencies
npm install
  1. Configure Firebase

Create a Firebase project at https://console.firebase.google.com

Enable the following services:

  • Firestore Database: Create database in production mode
  • Authentication: Enable Google provider and Anonymous authentication

Add Firestore security rules:

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    match /documents/{document} {
      allow read, write: if true;
    }
    match /presence/{document} {
      allow read, write: if true;
    }
  }
}
  1. Set up environment variables

Create a .env.local file in the root directory:

NEXT_PUBLIC_FIREBASE_API_KEY=your_api_key_here
NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN=your_project_id.firebaseapp.com
NEXT_PUBLIC_FIREBASE_PROJECT_ID=your_project_id
NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET=your_project_id.appspot.com
NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID=your_sender_id
NEXT_PUBLIC_FIREBASE_APP_ID=your_app_id

Get these values from Firebase Console → Project Settings → General → Your apps → Web app

  1. Run the development server
npm run dev

Open http://localhost:3000 in your browser.

  1. Build for production
npm run build
npm start

Testing Real-time Collaboration

  1. Open the application in two different browser windows (or use incognito mode)
  2. Sign in with different accounts (or one Google + one Guest)
  3. Create or open the same spreadsheet in both windows
  4. Edit cells in one window and observe real-time updates in the other
  5. Check presence indicators showing active users

🌐 Deployment

This project is deployed on Vercel for optimal Next.js performance.

👤 Author

Mayank Anand

📝 Assignment Details

This project was built as part of the Trademarkia Frontend Engineering Assignment. The assignment required:

  • Real-time collaborative spreadsheet editor
  • Formula support with cell references
  • Presence system for active users
  • Authentication (Google + Guest)
  • Document dashboard
  • Clean TypeScript code with no build errors
  • Deployed on Vercel

All core requirements and bonus features have been implemented.

🙏 Acknowledgments

  • Next.js team for the excellent framework
  • Firebase for real-time infrastructure
  • Tailwind CSS for the utility-first approach
  • Vercel for seamless deployment

Built with ❤️ for Trademarkia