logo
React
Supabase
Email.js
Node.js
DSA
Education

Mrill – DSA Learning Platform

January 28, 2024
7 min read
Source Code
Mrill – DSA Learning Platform

A full-stack web application for mastering 450+ DSA problems with authentication, difficulty categorization, progress tracking, and a responsive UI optimized for structured, efficient learning.

Introduction

Mrill is a comprehensive Data Structures and Algorithms (DSA) learning platform designed to help students and professionals master 450+ carefully curated problems. With a focus on structured learning and progress tracking, Mrill makes DSA preparation efficient and engaging.

The Learning Challenge

Many aspiring developers struggle with:

  • Finding quality DSA problems
  • Tracking learning progress
  • Staying motivated
  • Understanding difficulty progression
  • Organizing practice sessions

Mrill addresses these challenges with a structured, user-friendly approach to DSA mastery.

Core Features

📚 Curated Problem Set

450+ problems across all major topics:

  • Arrays and Strings
  • Linked Lists
  • Trees and Graphs
  • Dynamic Programming
  • Sorting and Searching
  • Stacks and Queues
  • Greedy Algorithms
  • Backtracking
  • And more...

🎯 Difficulty Categorization

Problems organized by difficulty:

  • Easy: Foundation building (150 problems)
  • Medium: Skill development (200 problems)
  • Hard: Expert challenges (100 problems)

📊 Progress Tracking

Comprehensive tracking system:

const ProgressTracker = ({ userId }) => {
  const [stats, setStats] = useState({
    total: 450,
    solved: 0,
    easy: { solved: 0, total: 150 },
    medium: { solved: 0, total: 200 },
    hard: { solved: 0, total: 100 }
  });

  const calculateProgress = () => {
    return (stats.solved / stats.total) * 100;
  };

  return (
    <div className="progress-container">
      <CircularProgress value={calculateProgress()} />
      <DifficultyBreakdown stats={stats} />
    </div>
  );
};

🔐 Authentication System

Secure user authentication with:

  • Email/password login
  • Social auth integration
  • Password recovery
  • Session management

Technical Architecture

Frontend - React

Modern React application with:

// Problem List Component
import React, { useState, useEffect } from 'react';
import { supabase } from '@/lib/supabaseClient';

const ProblemList = () => {
  const [problems, setProblems] = useState([]);
  const [filter, setFilter] = useState('all');

  useEffect(() => {
    fetchProblems();
  }, [filter]);

  const fetchProblems = async () => {
    let query = supabase
      .from('problems')
      .select('*');
    
    if (filter !== 'all') {
      query = query.eq('difficulty', filter);
    }
    
    const { data, error } = await query;
    if (!error) setProblems(data);
  };

  const markComplete = async (problemId) => {
    const { error } = await supabase
      .from('user_progress')
      .insert({
        user_id: userId,
        problem_id: problemId,
        completed_at: new Date()
      });
    
    if (!error) {
      // Update UI
      fetchProblems();
    }
  };

  return (
    <div className="problem-list">
      <FilterBar
        active={filter}
        onChange={setFilter}
      />
      {problems.map(problem => (
        <ProblemCard
          key={problem.id}
          problem={problem}
          onComplete={markComplete}
        />
      ))}
    </div>
  );
};

Backend - Supabase

Leveraging Supabase for:

Database Schema

-- Problems table
CREATE TABLE problems (
  id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
  title TEXT NOT NULL,
  description TEXT,
  difficulty VARCHAR(10) CHECK (difficulty IN ('easy', 'medium', 'hard')),
  topic VARCHAR(50),
  link TEXT,
  created_at TIMESTAMP DEFAULT NOW()
);

-- User progress table
CREATE TABLE user_progress (
  id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
  user_id UUID REFERENCES auth.users(id),
  problem_id UUID REFERENCES problems(id),
  completed_at TIMESTAMP DEFAULT NOW(),
  notes TEXT,
  UNIQUE(user_id, problem_id)
);

-- Create index for faster queries
CREATE INDEX idx_user_progress ON user_progress(user_id);
CREATE INDEX idx_problems_difficulty ON problems(difficulty);

Real-time Updates

// Subscribe to progress updates
const subscription = supabase
  .from('user_progress')
  .on('INSERT', payload => {
    updateProgressUI(payload.new);
  })
  .subscribe();

Email Integration - Email.js

Automated email notifications:

import emailjs from '@emailjs/browser';

export const sendProgressReport = async (userEmail, stats) => {
  const templateParams = {
    to_email: userEmail,
    problems_solved: stats.solved,
    total_problems: stats.total,
    progress_percentage: ((stats.solved / stats.total) * 100).toFixed(1)
  };

  try {
    await emailjs.send(
      'service_id',
      'template_id',
      templateParams,
      'public_key'
    );
    console.log('Report sent successfully');
  } catch (error) {
    console.error('Failed to send report:', error);
  }
};

Key Features in Detail

Responsive Design

Optimized for all devices:

/* Mobile-first approach */
.problem-card {
  padding: 1rem;
  margin: 0.5rem 0;
}

@media (min-width: 768px) {
  .problem-card {
    padding: 1.5rem;
    margin: 1rem 0;
  }
}

@media (min-width: 1024px) {
  .problem-list {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: 1rem;
  }
}

Smart Filtering

Filter problems by:

  • Difficulty level
  • Topic/category
  • Completion status
  • Bookmarked items
const FilterSystem = () => {
  const [filters, setFilters] = useState({
    difficulty: 'all',
    topic: 'all',
    status: 'all'
  });

  const applyFilters = (problems) => {
    return problems.filter(problem => {
      if (filters.difficulty !== 'all' && 
          problem.difficulty !== filters.difficulty) {
        return false;
      }
      if (filters.topic !== 'all' && 
          problem.topic !== filters.topic) {
        return false;
      }
      if (filters.status !== 'all') {
        const completed = userProgress.includes(problem.id);
        if (filters.status === 'completed' && !completed) return false;
        if (filters.status === 'pending' && completed) return false;
      }
      return true;
    });
  };

  return (
    <FilterControls
      filters={filters}
      onChange={setFilters}
    />
  );
};

Learning Path

Structured learning recommendation:

const LearningPath = ({ currentLevel, completedProblems }) => {
  const recommendNextProblems = () => {
    // Algorithm to suggest next problems
    const topics = analyzeWeakTopics(completedProblems);
    const difficulty = determineDifficulty(currentLevel);
    
    return supabase
      .from('problems')
      .select('*')
      .in('topic', topics)
      .eq('difficulty', difficulty)
      .not('id', 'in', completedProblems)
      .limit(5);
  };

  return (
    <div className="learning-path">
      <h2>Recommended Next Steps</h2>
      <RecommendedProblems />
    </div>
  );
};

User Experience Features

1. Problem Notes

Users can add personal notes to each problem:

const ProblemNotes = ({ problemId }) => {
  const [notes, setNotes] = useState('');

  const saveNotes = async () => {
    await supabase
      .from('user_progress')
      .update({ notes })
      .eq('problem_id', problemId)
      .eq('user_id', userId);
  };

  return (
    <textarea
      value={notes}
      onChange={(e) => setNotes(e.target.value)}
      onBlur={saveNotes}
      placeholder="Add your notes, approaches, or insights..."
    />
  );
};

2. Bookmarking System

Mark problems for later review:

const BookmarkButton = ({ problemId }) => {
  const [bookmarked, setBookmarked] = useState(false);

  const toggleBookmark = async () => {
    if (bookmarked) {
      await supabase
        .from('bookmarks')
        .delete()
        .match({ user_id: userId, problem_id: problemId });
    } else {
      await supabase
        .from('bookmarks')
        .insert({ user_id: userId, problem_id: problemId });
    }
    setBookmarked(!bookmarked);
  };

  return (
    <button onClick={toggleBookmark}>
      {bookmarked ? '⭐ Bookmarked' : '☆ Bookmark'}
    </button>
  );
};

3. Streak Tracking

Motivate consistent practice:

const StreakTracker = () => {
  const [streak, setStreak] = useState(0);

  const calculateStreak = async () => {
    const { data } = await supabase
      .from('user_progress')
      .select('completed_at')
      .eq('user_id', userId)
      .order('completed_at', { ascending: false });

    let currentStreak = 0;
    let lastDate = new Date();

    for (const record of data) {
      const recordDate = new Date(record.completed_at);
      const diffDays = Math.floor(
        (lastDate - recordDate) / (1000 * 60 * 60 * 24)
      );

      if (diffDays <= 1) {
        currentStreak++;
        lastDate = recordDate;
      } else {
        break;
      }
    }

    setStreak(currentStreak);
  };

  return (
    <div className="streak-badge">
      🔥 {streak} Day Streak
    </div>
  );
};

Performance Optimization

Lazy Loading

import { lazy, Suspense } from 'react';

const ProblemDetail = lazy(() => import('./ProblemDetail'));

function App() {
  return (
    <Suspense fallback={<LoadingSpinner />}>
      <ProblemDetail />
    </Suspense>
  );
}

Memoization

import { useMemo } from 'react';

const ProblemList = ({ problems, filters }) => {
  const filteredProblems = useMemo(() => {
    return applyFilters(problems, filters);
  }, [problems, filters]);

  return <List items={filteredProblems} />;
};

Deployment

Deployed with modern CI/CD:

# netlify.toml
[build]
  command = "npm run build"
  publish = "build"

[[redirects]]
  from = "/*"
  to = "/index.html"
  status = 200

Impact and Results

  • 450+ curated problems across all major DSA topics
  • Structured learning path for efficient progress
  • Engaging UI that keeps learners motivated
  • Progress analytics for informed learning decisions
  • Self-paced learning with flexible scheduling

Future Enhancements

  • Video explanations for complex problems
  • Coding playground for in-browser testing
  • Discussion forums for community learning
  • AI-powered hints for stuck users
  • Competition mode for friendly challenges
  • Mobile app for learning on the go

Conclusion

Mrill transforms DSA learning from an overwhelming task into a structured, trackable journey. By combining a comprehensive problem set with modern web technologies and user-centric design, Mrill empowers learners to achieve their coding interview and competitive programming goals.