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
1 change: 1 addition & 0 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ const App = () => {
<BrowserRouter>
<Routes>
<Route path="/" element={<Index />} />
<Route path="/recycle-bin" element={<Index deletePage={true} />} />
{/* ADD ALL CUSTOM ROUTES ABOVE THE CATCH-ALL "*" ROUTE */}
<Route path="*" element={<NotFound />} />
</Routes>
Expand Down
9 changes: 7 additions & 2 deletions src/components/NoteCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import {
AlertDialogTitle,
AlertDialogTrigger,
} from '@/components/ui/alert-dialog';

import { useLocation } from 'react-router-dom';
interface NoteCardProps {
note: Note;
isActive: boolean;
Expand All @@ -24,6 +24,8 @@ interface NoteCardProps {
}

export function NoteCard({ note, isActive, onClick, onDelete, onDuplicate }: NoteCardProps) {
const path = useLocation()?.pathname;
const deletePage = path === '/recycle-bin';
Copy link

Copilot AI Oct 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Inconsistent indentation. Line 28 should align with line 27.

Suggested change
const deletePage = path === '/recycle-bin';
const deletePage = path === '/recycle-bin';

Copilot uses AI. Check for mistakes.
const preview = note.content.slice(0, 100) || 'No content';

return (
Expand Down Expand Up @@ -83,7 +85,10 @@ export function NoteCard({ note, isActive, onClick, onDelete, onDuplicate }: Not
<AlertDialogHeader>
<AlertDialogTitle>Delete Note</AlertDialogTitle>
<AlertDialogDescription>
Are you sure you want to delete this note? This action cannot be undone.
{
deletePage ? "This note will be permanently deleted and cannot be recovered. Are you sure you want to continue?" :
"This note will be moved to the recycle bin and can be restored later. Are you sure you want to continue?"
}
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
Expand Down
8 changes: 7 additions & 1 deletion src/components/NoteEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
AlertDialogTitle,
AlertDialogTrigger,
} from '@/components/ui/alert-dialog';
import { useLocation } from 'react-router-dom';

interface NoteEditorProps {
note: Note;
Expand All @@ -25,6 +26,8 @@ interface NoteEditorProps {
export function NoteEditor({ note, onUpdate, onDelete }: NoteEditorProps) {
const [title, setTitle] = useState(note.title);
const [content, setContent] = useState(note.content);
const path = useLocation()?.pathname
const deletePage = path === '/recycle-bin'
Comment on lines +29 to +30
Copy link

Copilot AI Oct 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing semicolons at the end of these statements for consistency with the rest of the codebase.

Suggested change
const path = useLocation()?.pathname
const deletePage = path === '/recycle-bin'
const path = useLocation()?.pathname;
const deletePage = path === '/recycle-bin';

Copilot uses AI. Check for mistakes.

useEffect(() => {
setTitle(note.title);
Expand Down Expand Up @@ -60,7 +63,10 @@ export function NoteEditor({ note, onUpdate, onDelete }: NoteEditorProps) {
<AlertDialogHeader>
<AlertDialogTitle>Delete Note</AlertDialogTitle>
<AlertDialogDescription>
Are you sure you want to delete this note? This action cannot be undone.
{
deletePage ? "This note will be permanently deleted and cannot be recovered. Are you sure you want to continue?" :
"This note will be moved to the recycle bin and can be restored later. Are you sure you want to continue?"
}
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
Expand Down
115 changes: 74 additions & 41 deletions src/components/NotesSidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@ import { Note } from '@/types/note';
import { NoteCard } from './NoteCard';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { PlusCircle, Search, FileDown } from 'lucide-react';
import { PlusCircle, Search, FileDown, Trash2, Home } from 'lucide-react';
import jsPDF from 'jspdf';
import { useState } from 'react';
import { ScrollArea } from '@/components/ui/scroll-area';
import { ThemeToggle } from "@/components/theme/themeToggle";
import { Link, useLocation } from 'react-router-dom';

interface NotesSidebarProps {
notes: Note[];
Expand All @@ -26,62 +27,69 @@ export function NotesSidebar({
onDelete,
}: NotesSidebarProps) {
const [searchQuery, setSearchQuery] = useState('');
const path = useLocation()?.pathname;
const deletePage = path === '/recycle-bin';


const filteredNotes = notes.filter(
(note) =>
note.title.toLowerCase().includes(searchQuery.toLowerCase()) ||
note.content.toLowerCase().includes(searchQuery.toLowerCase())
(note.title.toLowerCase().includes(searchQuery.toLowerCase()) ||
note.content.toLowerCase().includes(searchQuery.toLowerCase())) && (!!note?.deleted === deletePage)
);

return (
<div className="w-screen md:w-80 border-r bg-secondary/30 flex flex-col h-screen">
<div className="p-4 border-b space-y-3">
<div className="flex items-center justify-between">
<h1 className="text-2xl font-bold text-foreground">Notes</h1>
<h1 className="text-2xl font-bold text-foreground">{ deletePage ? "Recycle Bin" : "Notes"}</h1>
<div className="flex items-center gap-2">
<ThemeToggle/>
<Button
onClick={() => {
if (!activeNoteId) return;
{!deletePage && (
<>
<ThemeToggle />
<Button
onClick={() => {
if (!activeNoteId) return;

const activeNote = notes.find(note => note.id === activeNoteId);
if (!activeNote) return;
const activeNote = notes.find(note => note.id === activeNoteId);
if (!activeNote) return;

const doc = new jsPDF();
const pageWidth = doc.internal.pageSize.getWidth();
const margin = 10;
const contentWidth = pageWidth - (margin * 2);
const doc = new jsPDF();
const pageWidth = doc.internal.pageSize.getWidth();
const margin = 10;
const contentWidth = pageWidth - (margin * 2);

doc.setFontSize(16);
doc.text(activeNote.title || 'Untitled Note', margin, margin);
doc.setFontSize(16);
doc.text(activeNote.title || 'Untitled Note', margin, margin);

doc.setFontSize(12);
const contentLines = doc.splitTextToSize(activeNote.content, contentWidth);
doc.setFontSize(12);
const contentLines = doc.splitTextToSize(activeNote.content, contentWidth);

let yOffset = margin + 10;
const lineHeight = 7;
let yOffset = margin + 10;
const lineHeight = 7;

contentLines.forEach((line: string) => {
if (yOffset > doc.internal.pageSize.getHeight() - margin) {
doc.addPage();
yOffset = margin;
}
doc.text(line, margin, yOffset);
yOffset += lineHeight;
});
contentLines.forEach((line: string) => {
if (yOffset > doc.internal.pageSize.getHeight() - margin) {
doc.addPage();
yOffset = margin;
}
doc.text(line, margin, yOffset);
yOffset += lineHeight;
});

doc.save(`${activeNote.title || 'note'}.pdf`);
}}
size="icon"
variant="outline"
className="hover:bg-secondary"
title="Export as PDF"
>
<FileDown className="h-5 w-5" />
</Button>
<Button onClick={onCreateNote} size="icon" variant="default" title="Create Note">
<PlusCircle className="h-5 w-5" />
</Button>
doc.save(`${activeNote.title || 'note'}.pdf`);
}}
size="icon"
variant="outline"
className="hover:bg-secondary"
title="Export as PDF"
>
<FileDown className="h-5 w-5" />
</Button>
<Button onClick={onCreateNote} size="icon" variant="default">
<PlusCircle className="h-5 w-5" />
</Button>
</>
)}
</div>
</div>
<div className="relative">
Expand All @@ -98,7 +106,7 @@ export function NotesSidebar({
<div className="p-4 space-y-2">
{filteredNotes.length === 0 ? (
<div className="text-center py-8 text-muted-foreground">
{searchQuery ? 'No notes found' : 'No notes yet. Create one!'}
{(searchQuery ? 'No notes found' : (deletePage ? 'Recycle Bin is empty' : 'No notes yet. Create one!'))}
</div>
) : (
filteredNotes.map((note) => (
Expand All @@ -114,6 +122,31 @@ export function NotesSidebar({
)}
</div>
</ScrollArea>
<div className="border-t">
{!deletePage ? (
<Link to="/recycle-bin" className="w-full">
<Button
variant="ghost"
size="sm"
className="w-full h-14 pl-4 justify-start text-red-600 hover:text-red-700 hover:bg-red-300 dark:hover:bg-red-950 rounded-none"
>
<Trash2 className="h-4 w-4 mr-2" />
Recycle Bin
</Button>
</Link>
) : (
<Link to="/" className="w-full">
<Button
variant="ghost"
size="sm"
className="w-full h-14 pl-4 justify-start rounded-none"
>
<Home className="h-4 w-4 mr-2" />
Home
</Button>
</Link>
)}
</div>
</div>
);
}
9 changes: 9 additions & 0 deletions src/hooks/useNotes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,14 @@ export function useNotes() {
setNotes((prev) => prev.filter((note) => note.id !== id));
};

const softDeleteNote = (id: string) => {
setNotes((prev) =>
prev.map((note) =>
note.id === id ? { ...note, deleted: true } : note
)
);
}

const duplicateNote = (id: string) => {
const newId = crypto.randomUUID();

Expand Down Expand Up @@ -123,6 +131,7 @@ export function useNotes() {
createNote,
updateNote,
deleteNote,
softDeleteNote,
duplicateNote,
};
}
23 changes: 16 additions & 7 deletions src/pages/Index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,8 @@
import { FileText, ArrowLeft } from "lucide-react";
import { toast } from "sonner";

const Index = () => {
const { notes, createNote, updateNote, deleteNote, duplicateNote } =
useNotes();
const Index = ({deletePage = false}: {deletePage?: boolean}) => {
const { notes, createNote, updateNote, deleteNote, duplicateNote, softDeleteNote } = useNotes();
const [activeNoteId, setActiveNoteId] = useState<string | null>(null);
const [isMobile, setIsMobile] = useState(false);

Expand All @@ -18,6 +17,10 @@
return () => window.removeEventListener("resize", checkMobile);
}, []);

useEffect(() => {
setActiveNoteId(null);
}, [deletePage]);

const handleCreateNote = () => {
const newId = createNote();
setActiveNoteId(newId);
Expand All @@ -29,6 +32,12 @@
setActiveNoteId(notes.length > 1 ? notes[0].id : null);
}
};
const handleSoftDeleteNote = (id: string) => {
softDeleteNote(id);
if (activeNoteId === id) {
setActiveNoteId(null);
}
};

const handleDuplicateNote = useCallback(
(id: string) => {
Expand Down Expand Up @@ -129,7 +138,7 @@
return () => {
document.removeEventListener("keydown", handleKeyDown, true);
};
}, [notes, activeNoteId]);

Check warning on line 141 in src/pages/Index.tsx

View workflow job for this annotation

GitHub Actions / ci (18.x)

React Hook useEffect has missing dependencies: 'handleCreateNote', 'handleDeleteNote', and 'navigateNotes'. Either include them or remove the dependency array

Check warning on line 141 in src/pages/Index.tsx

View workflow job for this annotation

GitHub Actions / ci (20.x)

React Hook useEffect has missing dependencies: 'handleCreateNote', 'handleDeleteNote', and 'navigateNotes'. Either include them or remove the dependency array

Check warning on line 141 in src/pages/Index.tsx

View workflow job for this annotation

GitHub Actions / ci (22.x)

React Hook useEffect has missing dependencies: 'handleCreateNote', 'handleDeleteNote', and 'navigateNotes'. Either include them or remove the dependency array

const activeNote = notes.find((note) => note.id === activeNoteId);

Expand All @@ -144,7 +153,7 @@
onSelectNote={setActiveNoteId}
onCreateNote={handleCreateNote}
onDuplicateNote={handleDuplicateNote}
onDelete={handleDeleteNote}
onDelete={ deletePage ? handleDeleteNote : handleSoftDeleteNote}
Copy link

Copilot AI Oct 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing space after opening brace in conditional expression. Should be onDelete={deletePage ? handleDeleteNote : handleSoftDeleteNote}

Copilot uses AI. Check for mistakes.
/>
<main className="flex-1 overflow-hidden relative">
{/* Keyboard Shortcuts Helper */}
Expand Down Expand Up @@ -193,7 +202,7 @@
<NoteEditor
note={activeNote}
onUpdate={updateNote}
onDelete={handleDeleteNote}
onDelete={ deletePage ? handleDeleteNote : handleSoftDeleteNote}
Copy link

Copilot AI Oct 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing space after opening brace in conditional expression. Should be onDelete={deletePage ? handleDeleteNote : handleSoftDeleteNote}

Suggested change
onDelete={ deletePage ? handleDeleteNote : handleSoftDeleteNote}
onDelete={deletePage ? handleDeleteNote : handleSoftDeleteNote}

Copilot uses AI. Check for mistakes.
/>
</div>
) : (
Expand Down Expand Up @@ -222,7 +231,7 @@
<NoteEditor
note={activeNote}
onUpdate={updateNote}
onDelete={handleDeleteNote}
onDelete={ deletePage ? handleDeleteNote : handleSoftDeleteNote}
Copy link

Copilot AI Oct 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing space after opening brace in conditional expression. Should be onDelete={deletePage ? handleDeleteNote : handleSoftDeleteNote}

Copilot uses AI. Check for mistakes.
/>
</div>
) : (
Expand All @@ -232,7 +241,7 @@
onSelectNote={setActiveNoteId}
onCreateNote={handleCreateNote}
onDuplicateNote={handleDuplicateNote}
onDelete={handleDeleteNote}
onDelete={ deletePage ? handleDeleteNote : handleSoftDeleteNote}
Copy link

Copilot AI Oct 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing space after opening brace in conditional expression. Should be onDelete={deletePage ? handleDeleteNote : handleSoftDeleteNote}

Suggested change
onDelete={ deletePage ? handleDeleteNote : handleSoftDeleteNote}
onDelete={deletePage ? handleDeleteNote : handleSoftDeleteNote}

Copilot uses AI. Check for mistakes.
/>
)}
</main>
Expand Down
1 change: 1 addition & 0 deletions src/types/note.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ export interface Note {
content: string;
createdAt: Date;
updatedAt: Date;
deleted?: boolean;
}
// Utility function to format timestamps
export const formatTimestamp = (date: Date, locale: string = 'en-US'): string => {
Expand Down
Loading