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.
