Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
182 changes: 178 additions & 4 deletions css/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -150,9 +150,61 @@ body {

/* Sidebar */
.sidebar {
background: var(--color-background-secondary);
border-right: 1px solid var(--color-border-tertiary);
padding: 20px 16px;
background: var(--color-background-secondary); border-right: 1px solid var(--color-border-tertiary);
padding: 20px 16px; display: flex; flex-direction: column; gap: 4px;
}
.sidebar-header { font-size: 11px; font-weight: 700; color: var(--color-text-tertiary); padding: 8px 12px; margin-bottom: 2px; letter-spacing: .06em; text-transform: uppercase; }
.nav-item { display: flex; align-items: center; gap: 12px; padding: 10px 12px; border-radius: var(--border-radius-md); cursor: pointer; font-size: 14px; font-weight: 500; color: var(--color-text-secondary); transition: all 0.2s ease; position: relative; }
.nav-item:hover { background: var(--color-background-primary); color: var(--color-text-primary); transform: translateX(2px); }
.nav-item.active { background: var(--color-background-primary); color: var(--color-text-primary); box-shadow: var(--shadow-sm); }
.nav-item.active::before { content: ''; position: absolute; left: 0; top: 15%; height: 70%; width: 3px; background: var(--color-text-primary); border-radius: 4px; }
.nav-dot { width: 10px; height: 10px; border-radius: 50%; flex-shrink: 0; box-shadow: inset 0 0 0 1px rgba(0,0,0,0.1); }
.badge { margin-left: auto; font-size: 12px; font-weight: 600; background: var(--color-background-tertiary); color: var(--color-text-secondary); padding: 2px 8px; border-radius: 12px; transition: all 0.2s; }
.nav-item:hover .badge { background: var(--color-background-primary); border: 1px solid var(--color-border-tertiary); }
.sidebar-divider { height: 1px; background: var(--color-border-tertiary); margin: 12px 0; }
.add-subject { font-size: 13px; font-weight: 500; color: var(--color-text-tertiary); padding: 8px 12px; cursor: pointer; transition: color 0.2s; display: flex; align-items: center; gap: 8px; }
.add-subject:hover { color: var(--color-text-primary); }

/* Main */
.main { display: flex; flex-direction: column; overflow: hidden; position: relative; }
.topbar { display: flex; align-items: center; gap: 12px; padding: 16px 24px; border-bottom: 1px solid var(--color-border-tertiary); flex-shrink: 0; background: rgba(var(--color-background-primary), 0.8); backdrop-filter: blur(10px); z-index: 10; }
.topbar-title { font-size: 18px; font-weight: 600; color: var(--color-text-primary); flex: 1; }
.btn { font-family: inherit; font-size: 13px; font-weight: 600; padding: 8px 16px; border: 1px solid var(--color-border-secondary); border-radius: var(--border-radius-sm); background: var(--color-background-primary); color: var(--color-text-primary); cursor: pointer; transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1); box-shadow: var(--shadow-sm); display: inline-flex; align-items: center; justify-content: center; gap: 6px; }
.btn:hover { background: var(--color-background-secondary); transform: translateY(-1px); box-shadow: var(--shadow-md); }
.btn:active { transform: translateY(0); box-shadow: 0 0 0; }
.btn-primary { background: var(--color-text-primary); color: var(--color-background-primary); border-color: transparent; }
.btn-primary:hover { background: var(--color-text-secondary); }

/* Calendar */
.cal-section { padding: 16px 24px; border-bottom: 1px solid var(--color-border-tertiary); flex-shrink: 0; }
.cal-header { display: flex; align-items: center; margin-bottom: 16px; justify-content:space-between; }
.cal-title { font-size: 15px; font-weight: 600; flex: 1; color: var(--color-text-primary); }
.cal-nav { font-size: 18px; color: var(--color-text-secondary); cursor: pointer; padding: 4px 8px; border-radius: 4px; transition: background 0.2s; }
.cal-nav:hover { background: var(--color-background-secondary); color: var(--color-text-primary); }
.cal-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 4px; text-align: center; }
.cal-day-label { font-size: 11px; color: var(--color-text-tertiary); padding-bottom: 8px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.05em; }
.cal-day { font-size: 13px; font-weight: 500; height: 32px; display: flex; align-items: center; justify-content: center; border-radius: var(--border-radius-md); cursor: pointer; color: var(--color-text-secondary); position: relative; transition: all 0.2s; }
.cal-day:hover { background: var(--color-background-secondary); color: var(--color-text-primary); transform: scale(1.1); z-index: 1; }
.cal-day.today { background: var(--color-text-primary); color: var(--color-background-primary); box-shadow: var(--shadow-sm); }
.cal-day-indicators { position: absolute; bottom: 3px; left: 0; right: 0; display: flex; justify-content: center; gap: 3px; }
.cal-day-indicator { width: 5px; height: 5px; border-radius: 50%; }
.cal-day.muted { color: var(--color-border-secondary); }
.cal-legend { display: flex; gap: 16px; margin-top: 16px; font-size: 11px; font-weight: 500; color: var(--color-text-tertiary); justify-content: flex-end; }
.cal-legend span { display: flex; align-items: center; gap: 6px; }
.legend-dot { width: 8px; height: 8px; border-radius: 50%; display: inline-block; }

/*Streak*/
.streak-display { display: flex; align-items: center; gap: 6px; padding: 8px 10px; border-radius: 12px; cursor: pointer; transition: all 0.2s ease; }
.streak-display:hover { background: rgba(0, 0, 0, 0.04); }
.streak-icon { width: 22px; height: 22px; object-fit: contain; }
#streak-count { font-size: 20px; font-weight: 700; color: var(--color-text-primary); }

/* Tasks */
.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; }
.tasks-actions-bar {
margin-top: 18px;
display: flex;
flex-direction: column;
gap: 4px;
Expand Down Expand Up @@ -2932,6 +2984,112 @@ body {
align-items: center;
gap: 12px;
}
/* Header right & Badges */

.header-right {
display: flex;
align-items: center;
gap: 10px;
}

.streak-badges {
display: flex;
align-items: center;
gap: 8px;

margin-right: 6px;

transform: translateY(1px);
}

.badge-wrapper {
width: 45px;
height: 45px;

display: flex;
align-items: center;
justify-content: center;

border-radius: 999px;

background: rgba(0, 0, 0, 0.025);

transition: all 0.2s ease;
}

.badge-wrapper:hover {
background: rgba(0, 0, 0, 0.05);
}

.streak-badge {
width: 30px;
height: 30px;

object-fit: contain;

transition: all 0.2s ease;
}

.streak-badge.locked {
filter: grayscale(1);
opacity: 0.32;
}

.streak-badge.unlocked {
opacity: 0.92;
}

.badge-wrapper:hover .streak-badge {
transform: scale(1.08);
}

.streak-display {
position: relative;
}

.streak-tooltip {
position: absolute;

top: 36px;
left: 50%;

transform: translateX(-50%) translateY(4px);

min-width: 220px;

padding: 10px 12px;

border-radius: 12px;

background: #111;
color: white;

font-size: 12px;
font-weight: 500;
line-height: 1.5;

text-align: center;

opacity: 0;
visibility: hidden;

transition: all 0.2s ease;

pointer-events: none;

z-index: 100;
}

.streak-display:hover .streak-tooltip {
opacity: 1;
visibility: visible;

transform: translateX(-50%) translateY(0);
}

.hidden {
display: none;
}

.logo {
width: 40px;
Expand All @@ -2947,7 +3105,7 @@ body {

.header-nav {
display: flex;
gap: 24px;
gap: 20px;
}

.header-nav a {
Expand All @@ -2962,6 +3120,7 @@ body {
}

.profile-btn {
height:40px;
padding: 8px 16px;
border: none;
border-radius: var(--border-radius-md);
Expand Down Expand Up @@ -3934,6 +4093,7 @@ body {
min-width: 48px;
}
.summary-box {
position:relative;
margin-top: 16px;
padding: 14px;
background: var(--color-background-secondary);
Expand Down Expand Up @@ -4161,6 +4321,19 @@ 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;
Expand Down Expand Up @@ -4700,6 +4873,7 @@ body {
font-weight: 600;
}


/* Footer */
.site-footer {
width: 100%;
Expand Down
37 changes: 34 additions & 3 deletions database.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ const db = new sqlite3.Database(path.join(__dirname, 'studyplan.db'));

function initDb() {
db.serialize(() => {

// Subjects Table
db.run(`CREATE TABLE IF NOT EXISTS subjects (
id TEXT PRIMARY KEY,
Expand All @@ -14,9 +15,18 @@ function initDb() {
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
)`);

// Tasks Table
// ================= USERS TABLE =================
db.run(`CREATE TABLE IF NOT EXISTS users (
id TEXT PRIMARY KEY,
email TEXT UNIQUE NOT NULL,
password TEXT NOT NULL,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
)`);

// ================= TASKS TABLE =================
db.run(`CREATE TABLE IF NOT EXISTS tasks (
id TEXT PRIMARY KEY,
user_email TEXT,
subject_id TEXT,
title TEXT NOT NULL,
description TEXT,
Expand All @@ -33,6 +43,21 @@ function initDb() {
FOREIGN KEY (subject_id) REFERENCES subjects(id)
)`);

// Add labels column to existing tasks table if it doesn't exist
db.all("PRAGMA table_info(tasks)", (err, rows) => {
if (err) return;

const hasLabels = rows.some(r => r.name === 'labels');
if (!hasLabels) {
db.run("ALTER TABLE tasks ADD COLUMN labels TEXT DEFAULT '[]'");
}

// ================= ADD USER EMAIL COLUMN =================
const hasUserEmail = rows.some(r => r.name === 'user_email');
if (!hasUserEmail) {
db.run("ALTER TABLE tasks ADD COLUMN user_email TEXT");
}
});
db.all("PRAGMA table_info(tasks)", (err, rows) => {
if (err) return;

Expand All @@ -59,15 +84,21 @@ db.all("PRAGMA table_info(tasks)", (err, rows) => {
db.get('SELECT COUNT(*) as count FROM subjects', (err, row) => {
if (row && row.count === 0) {
console.log("Seeding subjects...");
const stmt = db.prepare("INSERT INTO subjects (id, name, short_code, color) VALUES (?, ?, ?, ?)");

const stmt = db.prepare(
"INSERT INTO subjects (id, name, short_code, color) VALUES (?, ?, ?, ?)"
);

stmt.run('sub_1', 'Computer Science', 'CS', 'var(--color-text-info)');
stmt.run('sub_2', 'Mathematics', 'Maths', 'var(--color-text-success)');
stmt.run('sub_3', 'English Lit', 'English', 'var(--color-text-purple)');
stmt.run('sub_4', 'Physics', 'Physics', 'var(--color-text-warning)');

stmt.finalize();
}
});

});
}

module.exports = { db, initDb };
module.exports = { db, initDb };
Loading