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
27 changes: 19 additions & 8 deletions app.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,19 @@
require('dotenv').config();
console.log("✅ MONGODB_URI from .env is:", process.env.MONGODB_URI);

const express = require('express');
const expressLayouts = require('express-ejs-layouts');
const expressLayouts = require('express-ejs-layouts');
const methodOverride = require('method-override');
const connectDB = require('./server/config/db');
const session = require('express-session');
const passport = require('passport');
const MongoStore = require('connect-mongo');

const app = express();
const port = 5000 || process.env.PORT;
const port = 5001 || process.env.PORT;

app.use(session({
secret:'keyboard cat',
secret: 'keyboard cat',
resave: false,
saveUninitialized: true,
store: MongoStore.create({
Expand All @@ -23,8 +24,19 @@ app.use(session({
app.use(passport.initialize());
app.use(passport.session());

// 🧪 MOCK USER: Use this ONLY for local testing
app.use((req, res, next) => {
req.user = {
name: "Test User",
email: "testuser@example.com",
id: "testuser123"
};
next();
});


//middlewares
app.use(express.urlencoded({extended:true}));
app.use(express.urlencoded({ extended: true }));
app.use(express.json());
app.use(methodOverride("_method"));

Expand All @@ -45,11 +57,10 @@ app.use('/', require('./server/routes/index'));
app.use('/', require('./server/routes/dashboard'));

//handle 404
app.get('*',function(req,res) {
// res.status(404).send('404 page not found')
res.status(404).render('404');
app.get('*', function (req, res) {
res.status(404).render('404');
})

app.listen(port, () => {
console.log(`App listening on ${port}`);
})
});
86 changes: 73 additions & 13 deletions server/controllers/dashboardController.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,29 +18,23 @@ exports.dashboard = async (req, res) => {

try {
// Mongoose "^7.0.0 Update
const notes = await Note.aggregate([
{ $sort: { updatedAt: -1 } },
{ $match: { user: new mongoose.Types.ObjectId(req.user.id) } },
{
$project: {
title: { $substr: ["$title", 0, 30] },
body: { $substr: ["$body", 0, 100] },
},
},
])
const notes = await Note.find({ user: req.user.id, archived: false })
.sort({ updatedAt: -1 })
.skip(perPage * page - perPage)
.limit(perPage)
.exec();

const count = await Note.countDocuments();
const count = await Note.countDocuments({ user: req.user.id, archived: false });
const archivedCount = await Note.countDocuments({ user: req.user.id, archived: true });

res.render('dashboard/index', {
userName: req.user.firstName,
locals,
notes,
layout: "../views/layouts/dashboard",
current: page,
pages: Math.ceil(count / perPage)
pages: Math.ceil(count / perPage),
archivedCount
});
} catch (error) {
console.log(error);
Expand Down Expand Up @@ -267,4 +261,70 @@ exports.dashboardSummarizeNote = async (req, res) => {
} catch (error) {
console.error('Summarize Error:', error.message);
res.status(500).json({ summary: "Failed to generate summary." });
}}

}
};

// Archive a note
exports.archiveNote = async (req, res) => {
try {
const note = await Note.findOneAndUpdate(
{ _id: req.params.id, user: req.user.id },
{ archived: true, archivedAt: new Date() },
{ new: true }
);
if (!note) return res.status(404).send('Note not found');
req.flash('success_msg', 'Note archived successfully.');
res.redirect('/dashboard');
} catch (err) {
res.status(500).send('Server Error');
}
};

// Restore a note
exports.restoreNote = async (req, res) => {
try {
const note = await Note.findOneAndUpdate(
{ _id: req.params.id, user: req.user.id },
{ archived: false, archivedAt: null },
{ new: true }
);
if (!note) return res.status(404).send('Note not found');
req.flash('success_msg', 'Note restored successfully.');
res.redirect('/dashboard/archived');
} catch (err) {
res.status(500).send('Server Error');
}
};

// Permanently delete a note
exports.deleteNotePermanently = async (req, res) => {
try {
const note = await Note.findOneAndDelete({ _id: req.params.id, user: req.user.id, archived: true });
if (!note) return res.status(404).send('Note not found or not archived');
req.flash('success_msg', 'Note permanently deleted.');
res.redirect('/dashboard/archived');
} catch (err) {
res.status(500).send('Server Error');
}
};

// GET /dashboard/archived - Show archived notes
exports.dashboardArchivedNotes = async (req, res) => {
try {
const archivedNotes = await Note.find({ user: req.user.id, archived: true })
.sort({ archivedAt: -1 })
.lean();
const archivedCount = archivedNotes.length;
res.render('dashboard/archived', {
userName: req.user.firstName,
archivedNotes,
layout: '../views/layouts/dashboard',
archivedCount
});
} catch (error) {
console.log(error);
res.status(500).send('Server Error');
}
};

7 changes: 7 additions & 0 deletions server/models/Notes.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,13 @@ const NoteSchema = new schema({
type: String,
default: '',
},
archived: {
type: Boolean,
default: false
},
archivedAt: {
type: Date
},
createdAt:{
type:Date,
default: Date.now()
Expand Down
26 changes: 26 additions & 0 deletions server/routes/dashboard.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,36 @@ router.post('/dashboard/add',isLoggedIn, dashboardController.dashboardAddNote);
router.get('/dashboard/search',isLoggedIn, dashboardController.dashboardSearch);
router.post('/dashboard/search',isLoggedIn, dashboardController.dashboardSearchSubmit);


// Archived notes page
router.get('/dashboard/archived', isLoggedIn, dashboardController.dashboardArchivedNotes);

// Archive a note
router.patch('/notes/:id/archive', dashboardController.archiveNote);
// Restore a note
router.patch('/notes/:id/restore', dashboardController.restoreNote);
// Permanently delete a note
router.delete('/notes/:id/permanent', dashboardController.deleteNotePermanently);

// No authentication middleware here
router.post('/autosave', (req, res) => {
// Just log or mock-save the note
console.log('Auto-saving note:', req.body.content);
res.json({ success: true });
});

router.post('/archived', (req, res) => {
// Just log or mock-archive the note
console.log('Archiving note:', req.body.content);
res.json({ success: true });
});

=======
// EXPORT FEATURE: Route to export all notes for the current user
router.get('/dashboard/export',isLoggedIn, dashboardController.dashboardExport);

// EXPORT FEATURE: Route to export individual note by ID
router.get('/dashboard/export/:id',isLoggedIn, dashboardController.dashboardExportNote);


module.exports = router;
40 changes: 40 additions & 0 deletions views/dashboard/archived.ejs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<div class="container-fluid container-fluid-custom pb-5 mb-5" style="background: #f7f7f7; min-height: 90vh;">
<div class="row mb-4">
<div class="col">
<h1>Archived Notes</h1>
</div>
<div class="col text-end">
<a href="/dashboard" class="btn btn-outline-primary">Back to Dashboard</a>
</div>
</div>
<div class="row">
<% if(archivedNotes.length > 0) { %>
<% archivedNotes.forEach(function(note) { %>
<div class="col-sm-4 mb-4">
<div class="card border-secondary" style="background: #ececec; min-height: 180px;">
<div class="card-body">
<h5 class="card-title"><%= note.title %></h5>
<p class="card-text"><%= note.body.length > 100 ? note.body.substring(0, 100) + '...' : note.body %></p>
<div class="d-flex justify-content-between align-items-center mt-3">
<form action="/notes/<%= note._id %>/restore?_method=PATCH" method="POST" class="me-2">
<button type="submit" class="btn btn-success btn-sm" title="Restore this note" style="border-radius: 20px;">
<span aria-hidden="true">&#x21A9;</span> Restore
</button>
</form>
<form action="/notes/<%= note._id %>/permanent?_method=DELETE" method="POST" onsubmit="return confirm('Are you sure you want to permanently delete this note? This cannot be undone.');">
<button type="submit" class="btn btn-danger btn-sm" title="Delete permanently" style="border-radius: 20px;">
<span aria-hidden="true">🗑️</span> Delete
</button>
</form>
</div>
</div>
</div>
</div>
<% }); %>
<% } else { %>
<div class="col-12 text-center mt-5">
<h4>No archived notes found.</h4>
</div>
<% } %>
</div>
</div>
9 changes: 9 additions & 0 deletions views/dashboard/index.ejs
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,17 @@
<% for(var i = 0; i < notes.length; i++){ %>

<div class="col-sm-3 mb-4">

<div class="card border-primary position-relative" style="min-height: 210px">
<form action="/notes/<%= notes[i]._id %>/archive?_method=PATCH" method="POST" style="position: absolute; top: 8px; right: 8px; z-index: 2;">
<button type="submit" class="btn btn-sm btn-outline-secondary" title="Archive this note" style="border-radius: 50%;">
<span aria-hidden="true">🗃️</span>
</button>
</form>

<div class="card border-primary" style="min-height: 210px">
<!-- Note content area - clickable to view/edit note -->

<a href="/dashboard/item/<%= notes[i]._id %>" class="card-body text-decoration-none">
<h5 class="card-title"><%= notes[i].title %></h5>
<p class="card-text"><%= notes[i].body %></p>
Expand Down
10 changes: 10 additions & 0 deletions views/partials/header_dashboard.ejs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,16 @@
NotesApp
</a>

<nav class="nav col-12 col-md-auto mb-2 justify-content-center mb-md-0">
<a href="/dashboard" class="nav-link px-3">Dashboard</a>
<a href="/dashboard/archived" class="nav-link px-3">
<span aria-hidden="true">🗃️</span> Archived
<% if(typeof archivedCount !== 'undefined' && archivedCount > 0) { %>
<span class="badge bg-secondary"><%= archivedCount %></span>
<% } %>
</a>
</nav>

<form class="nav col-12 col-md-auto flex-fill mb-2 justify-content-center mb-md-0" role="search" method="POST" action="/dashboard/search">
<input type="search" name="searchTerm" class="form-control border-primary" placeholder="Search..." aria-label="Search">
</form>
Expand Down