diff --git a/css/index.css b/css/index.css
index 28fbfd3..bb03955 100644
--- a/css/index.css
+++ b/css/index.css
@@ -4161,12 +4161,64 @@ body {
.tasks-section { flex: 1; overflow-y: auto; padding: 0 24px 24px; scroll-behavior: smooth; }
.tasks-section::-webkit-scrollbar { width: 6px; }
.tasks-section::-webkit-scrollbar-thumb { background: var(--color-border-secondary); border-radius: 10px; }
+.profile-section { flex: 1; overflow-y: auto; padding: 24px; display: flex; flex-direction: column; gap: 24px; }
+.profile-header { display: flex; justify-content: space-between; align-items: flex-start; gap: 20px; }
+.profile-page-title { font-size: 24px; font-weight: 700; color: var(--color-text-primary); }
+.profile-page-subtitle { margin: 8px 0 0; color: var(--color-text-secondary); max-width: 640px; line-height: 1.6; }
+.profile-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 20px; }
+.profile-card { background: var(--color-background-primary); border: 1px solid var(--color-border-tertiary); border-radius: var(--border-radius-md); padding: 24px; box-shadow: var(--shadow-sm); }
+.profile-card h2 { margin: 0 0 16px; font-size: 16px; font-weight: 700; color: var(--color-text-primary); }
+.profile-field { display: flex; justify-content: space-between; gap: 16px; margin-bottom: 14px; font-size: 14px; color: var(--color-text-secondary); }
+.profile-field-label { color: var(--color-text-tertiary); font-weight: 600; }
+.profile-stats { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 14px; }
+.profile-stat-value { display: block; font-size: 22px; font-weight: 700; color: var(--color-text-primary); margin-bottom: 4px; }
+.profile-summary-card p { margin: 0; color: var(--color-text-secondary); line-height: 1.8; }
+@media (max-width: 900px) { .profile-grid { grid-template-columns: 1fr; } }
.tasks-actions-bar {
margin-top: 18px;
display: flex;
gap: 12px;
}
+.subject-filter-bar {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 10px;
+ align-items: center;
+ padding: 0 24px 12px;
+}
+
+.subject-filter-bar .filter-label {
+ color: var(--color-text-secondary);
+ font-size: 13px;
+ font-weight: 600;
+ white-space: nowrap;
+}
+
+.subject-filter-chips {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 8px;
+}
+
+.subject-filter-chip {
+ border: 1px solid var(--color-border-tertiary);
+ background: var(--color-background-primary);
+ color: var(--color-text-primary);
+ padding: 8px 12px;
+ border-radius: 999px;
+ cursor: pointer;
+ transition: background 0.2s ease, border-color 0.2s ease, color 0.2s ease;
+ font-size: 13px;
+}
+
+.subject-filter-chip:hover,
+.subject-filter-chip.active {
+ background: var(--color-background-secondary);
+ border-color: var(--color-text-primary);
+ color: var(--color-text-primary);
+}
+
.extract-preview {
overflow-y: auto;
padding: 0 20px 12px;
diff --git a/index.html b/index.html
index 543be76..7801e23 100644
--- a/index.html
+++ b/index.html
@@ -62,7 +62,7 @@
StudyPlan
@@ -161,6 +161,12 @@ StudyPlan
0 mins
+
+
+
@@ -207,6 +213,61 @@
StudyPlan
+
+
+
+
+
+
+ Account details
+
+ Username
+ StudyPlan User
+
+
+ Email
+ user@studyplan.app
+
+
+ Member since
+ June 2026
+
+
+
+
+ Study statistics
+
+
+ 0
+ Completed
+
+
+ 0
+ Pending
+
+
+ 0
+ Archived
+
+
+ 0
+ Subjects
+
+
+
+
+
+
+ Account overview
+ Your profile information and study statistics will update automatically as you use StudyPlan.
+
+
+
diff --git a/js/app.js b/js/app.js
index 2bc3bce..2c8f637 100644
--- a/js/app.js
+++ b/js/app.js
@@ -99,6 +99,8 @@ const downloadBtn = document.getElementById('download-btn');
const calendarDownloadBtn = document.getElementById('calendar-download-btn');
const newTaskBtn = document.getElementById('add-task-btn');
const labelFilterSelect = document.getElementById('label-filter');
+const subjectFilterChips = document.getElementById('subject-filter-chips');
+let selectedSubjectFilter = '';
if (labelFilterSelect) {
labelFilterSelect.addEventListener('change', (e) => {
@@ -218,6 +220,34 @@ const newTaskCancel = document.getElementById('new-task-cancel');
const newTaskSave = document.getElementById('new-task-save');
const newTaskEstimatedDuration = document.getElementById('new-task-estimated-duration');
const newTaskDurationSwitch = document.getElementById('new-task-duration-switch');
+
+function renderSubjectFilterBar(tasks, subjects) {
+ if (!subjectFilterChips) return;
+
+ const subjectMap = new Map();
+ tasks.forEach(t => {
+ const id = t.subject_id || '';
+ if (!subjectMap.has(id)) {
+ const subject = subjects.find(s => s.id === id);
+ subjectMap.set(id, { id, name: subject ? subject.name : id ? 'General' : 'General' });
+ }
+ });
+
+ const chips = [{ id: '', name: 'All' }, ...Array.from(subjectMap.values()).sort((a, b) => a.name.localeCompare(b.name))];
+
+ subjectFilterChips.innerHTML = chips.map(subject => `
+
+ `).join('');
+
+ subjectFilterChips.querySelectorAll('.subject-filter-chip').forEach(btn => {
+ btn.addEventListener('click', () => {
+ selectedSubjectFilter = btn.dataset.subjectId || '';
+ renderTasks();
+ });
+ });
+}
const newTaskDurationMin = document.getElementById('new-task-duration-min');
const newTaskDurationHr = document.getElementById('new-task-duration-hr');
let selectedTaskDurationUnit = 'minutes';
@@ -578,6 +608,89 @@ function renderFocusTasks() {
}
}
+function renderProfileSection() {
+ if (!profileSection) return;
+
+ const tasks = store.tasks || [];
+ const subjects = store.subjects || [];
+ const completedCount = tasks.filter(t => t.status === 'Done').length;
+ const pendingCount = tasks.filter(t => t.status !== 'Done' && !t.archived).length;
+ const archivedCount = tasks.filter(t => t.archived).length;
+ const subjectsCount = subjects.length;
+ const username = localStorage.getItem('studyplan_username') || 'StudyPlan User';
+ const email = localStorage.getItem('studyplan_email') || 'user@studyplan.app';
+ const joinedDate = localStorage.getItem('studyplan_joined') || 'June 2026';
+
+ profileSection.innerHTML = `
+
+
+
+
+ Account details
+
+ Username
+ ${escapeHtml(username)}
+
+
+ Email
+ ${escapeHtml(email)}
+
+
+ Member since
+ ${escapeHtml(joinedDate)}
+
+
+
+
+ Study statistics
+
+
+ ${completedCount}
+ Completed
+
+
+ ${pendingCount}
+ Pending
+
+
+ ${archivedCount}
+ Archived
+
+
+ ${subjectsCount}
+ Subjects
+
+
+
+
+
+
+ Account overview
+ Your profile information and study statistics will update automatically as you use StudyPlan.
+
+ `;
+}
+
+function showProfileSection() {
+ currentView = 'profile';
+ document.querySelector('.cal-section')?.classList.add('hidden');
+ document.getElementById('tasks-section')?.classList.add('hidden');
+ document.getElementById('focus-section')?.classList.add('hidden');
+ profileSection?.classList.remove('hidden');
+ topbar?.classList.add('hidden');
+ renderProfileSection();
+}
+
+function hideProfileSection() {
+ profileSection?.classList.add('hidden');
+ topbar?.classList.remove('hidden');
+}
+
function formatDate(dateStr) {
if (!dateStr) return 'No Date';
const d = new Date(dateStr);
@@ -653,9 +766,14 @@ function renderTasks() {
}
const displayTasksRaw = currentView === 'archived' ? archivedTasks : activeTasks;
- const displayTasks = activeLabelFilter
+ const displayTasksLabelFiltered = activeLabelFilter
? displayTasksRaw.filter(t => t.labels && t.labels.includes(activeLabelFilter))
: displayTasksRaw;
+ const displayTasks = selectedSubjectFilter
+ ? displayTasksLabelFiltered.filter(t => String(t.subject_id) === String(selectedSubjectFilter))
+ : displayTasksLabelFiltered;
+
+ renderSubjectFilterBar(displayTasksRaw, subjects);
// Extract unique labels to populate the filter dropdown
if (labelFilterSelect) {
@@ -1219,6 +1337,7 @@ store.subscribe(renderTasks);
store.subscribe(renderExtraction);
store.subscribe(renderCalendar);
store.subscribe(renderFocusTasks);
+store.subscribe(renderProfileSection);
store.subscribe(renderSidebarSubjects);
document.addEventListener('DOMContentLoaded', () => {
@@ -1291,6 +1410,7 @@ document.addEventListener('DOMContentLoaded', () => {
calendarBtn.addEventListener('click', () => {
currentView = 'calendar';
+ hideProfileSection();
document.querySelector('.cal-section').classList.remove('hidden');
document.getElementById('tasks-section').classList.remove('hidden');
document.getElementById('focus-section').classList.add('hidden');
@@ -1300,6 +1420,7 @@ document.addEventListener('DOMContentLoaded', () => {
allTasksBtn.addEventListener('click', () => {
currentView = 'all-tasks';
+ hideProfileSection();
document.querySelector('.cal-section').classList.add('hidden');
document.getElementById('tasks-section').classList.remove('hidden');
document.getElementById('focus-section').classList.add('hidden');
@@ -1309,6 +1430,7 @@ document.addEventListener('DOMContentLoaded', () => {
archivedTasksBtn.addEventListener('click', () => {
currentView = 'archived';
+ hideProfileSection();
document.querySelector('.cal-section').classList.add('hidden');
document.getElementById('tasks-section').classList.remove('hidden');
document.getElementById('focus-section').classList.add('hidden');
@@ -1319,6 +1441,7 @@ document.addEventListener('DOMContentLoaded', () => {
if(focusModeBtn) {
focusModeBtn.addEventListener('click', () => {
currentView = 'focus';
+ hideProfileSection();
document.querySelector('.cal-section').classList.add('hidden');
document.getElementById('tasks-section').classList.add('hidden');
document.getElementById('focus-section').classList.remove('hidden');
@@ -1326,12 +1449,21 @@ document.addEventListener('DOMContentLoaded', () => {
renderFocusTasks();
});
}
-
- document.getElementById('cal-next').addEventListener('click', () => {
- currentMonthDate.setMonth(currentMonthDate.getMonth() + 1);
- renderCalendar();
+if (profileBtn) {
+ profileBtn.addEventListener('click', () => {
+ showProfileSection();
});
+}
+
+document.getElementById('cal-prev').addEventListener('click', () => {
+ currentMonthDate.setMonth(currentMonthDate.getMonth() - 1);
+ renderCalendar();
+});
+document.getElementById('cal-next').addEventListener('click', () => {
+ currentMonthDate.setMonth(currentMonthDate.getMonth() + 1);
+ renderCalendar();
+});
//NEw Task addition event listeners
newTaskBtn.addEventListener('click', () => {