Skip to content
Closed
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
52 changes: 52 additions & 0 deletions NoteItem_updates.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// Add these imports at the top of NoteItem.js
import remarkMath from 'remark-math';
import rehypeKatex from 'rehype-katex';
import 'katex/dist/katex.min.css';

// Update the ReactMarkdown component to include math plugins:
<ReactMarkdown
remarkPlugins={[remarkGfm, remarkMath]}
rehypePlugins={[rehypeKatex]}
components={customRenderers}
>
{note.description}
</ReactMarkdown>

// Add table renderers to customRenderers object:
table: ({node, ...props}) => (
<div className="overflow-x-auto my-4">
<table className="min-w-full border-collapse border border-gray-600" {...props} />
</div>
),
thead: ({node, ...props}) => <thead className="bg-gray-800" {...props} />,
tbody: ({node, ...props}) => <tbody className="bg-gray-900" {...props} />,
tr: ({node, ...props}) => <tr className="border-b border-gray-700" {...props} />,
th: ({node, ...props}) => <th className="border border-gray-600 px-4 py-2 text-left font-semibold text-white" {...props} />,
td: ({node, ...props}) => <td className="border border-gray-600 px-4 py-2 text-gray-300" {...props} />,

// Add PDF attachments display section (add after images section):
{note.attachments && note.attachments.length > 0 && (
<div className="mt-4 pt-4 border-t border-gray-700">
<h4 className="text-sm font-semibold text-gray-300 mb-2">📎 Attachments</h4>
<div className="space-y-2">
{note.attachments.map((attachment, index) => (
<a
key={index}
href={attachment.url}
target="_blank"
rel="noopener noreferrer"
className="flex items-center gap-2 p-2 bg-gray-800 hover:bg-gray-750 rounded-lg transition-colors"
>
<span className="text-2xl">📄</span>
<div className="flex-1 min-w-0">
<p className="text-sm font-medium text-white truncate">{attachment.name}</p>
<p className="text-xs text-gray-400">{(attachment.size / 1024 / 1024).toFixed(2)} MB</p>
</div>
<svg className="w-4 h-4 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14" />
</svg>
</a>
))}
</div>
</div>
)}
458 changes: 458 additions & 0 deletions add_study_features.sh

Large diffs are not rendered by default.

85 changes: 85 additions & 0 deletions savebook/activate_features.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
#!/bin/bash

echo "🎓 SaveBook Study Notes Enhancement - Activation Script"
echo "========================================================"
echo ""

# Check if we're in the right directory
if [ ! -f "package.json" ]; then
echo "❌ Error: Please run this script from the savebook directory"
echo " cd /mnt/d/Desktop/JWOC/SaveBook/savebook"
exit 1
fi

echo "📦 Step 1: Checking dependencies..."
if ! grep -q "rehype-katex" package.json; then
echo "⚠️ Dependencies not found. Installing..."
npm install rehype-katex remark-math katex
else
echo "✅ Dependencies already installed"
fi

echo ""
echo "📝 Step 2: Activating enhanced AddNote component..."

if [ -f "components/notes/AddNoteEnhanced.js" ]; then
# Backup original if not already backed up
if [ ! -f "components/notes/AddNote.js.backup" ]; then
echo "💾 Creating backup of original AddNote.js..."
cp components/notes/AddNote.js components/notes/AddNote.js.backup
fi

# Replace with enhanced version
echo "🔄 Replacing AddNote.js with enhanced version..."
cp components/notes/AddNoteEnhanced.js components/notes/AddNote.js
echo "✅ AddNote.js updated successfully!"
else
echo "❌ Error: AddNoteEnhanced.js not found"
echo " Please run add_study_features.sh first"
exit 1
fi

echo ""
echo "🎨 Step 3: Checking component files..."
echo "✅ Notes model updated (attachments field added)"
echo "✅ API route created (app/api/upload/attachments/route.js)"
echo "✅ NoteState context updated (attachments support)"

echo ""
echo "🚀 Step 4: Starting development server..."
echo ""
echo "Run: npm run dev"
echo ""
echo "Then navigate to: http://localhost:3000"
echo ""

echo "✨ Features Available:"
echo " ✓ Math formulas (LaTeX) - Use \$E=mc^2\$ or \$\$...\$\$"
echo " ✓ Tables for comparisons - Use | Header | syntax"
echo " ✓ Image uploads - Already existed, enhanced"
echo " ✓ PDF attachments - NEW! Upload reference materials"
echo " ✓ Study Notes template - Click 📚 Study Notes button"
echo ""

echo "📖 Documentation:"
echo " - FEATURE_SUMMARY.md - Complete feature documentation"
echo " - IMPLEMENTATION_GUIDE.md - Technical implementation details"
echo " - DEMO_NOTE.md - Copy-paste demo with all features"
echo ""

echo "🎉 Setup Complete! Happy note-taking!"
echo ""
echo "💡 Quick Test:"
echo " 1. Go to Add Note page"
echo " 2. Click '📚 Study Notes' template"
echo " 3. Click 'Preview' to see rendered output"
echo " 4. Try adding: \$E = mc^2\$ in description"
echo " 5. Upload a PDF file"
echo ""

# Ask if user wants to start dev server
read -p "Start development server now? (y/n) " -n 1 -r
echo
if [[ $REPLY =~ ^[Yy]$ ]]; then
npm run dev
fi
2 changes: 1 addition & 1 deletion savebook/app/(auth)/login/page.js
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ const LoginForm = () => {
onClick={() => window.location.href = "/api/auth/github"}
className="w-full flex items-center justify-center gap-3 bg-gray-700 hover:bg-gray-600 text-white py-3 rounded-lg transition-colors"
>
<svg className="w-5 h-5" fill="currentColor" viewBox="0 0 24 24"><path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.041-1.412-4.041-1.412-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z"/></svg>
<svg className="w-5 h-5" fill="currentColor" viewBox="0 0 24 24"><path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.041-1.412-4.041-1.412-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z" /></svg>
Continue with GitHub
</button>

Expand Down
131 changes: 55 additions & 76 deletions savebook/app/(auth)/register/page.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,21 @@ import { useRouter } from 'next/navigation';
import React, { useState, useEffect } from 'react';
import toast from 'react-hot-toast';
import Link from 'next/link';

import { Eye, EyeOff } from 'lucide-react';

// Signup Form Component
const SignupForm = () => {
const { register, isAuthenticated } = useAuth();
const [credentials, setCredentials] = useState({
username: '',
email: '',
password: '',
confirmPassword: ''
});
const [errors, setErrors] = useState({
username: '',
password: '',
confirmPassword: ''
confirmPassword: '',
name: '',
course: '',
subjectsOfInterest: ''
});
const [errors, setErrors] = useState({});
const [isLoading, setIsLoading] = useState(false);
const [showPassword, setShowPassword] = useState(false);
const [showConfirmPassword, setShowConfirmPassword] = useState(false);
Expand All @@ -44,14 +43,23 @@ const SignupForm = () => {
// Validate and collect errors
const newErrors = {};

if (!credentials.username.trim()) {
newErrors.username = 'Username is required';
if (!credentials.username.trim()) newErrors.username = 'Username is required';
if (!credentials.name.trim()) newErrors.name = 'Full Name is required';

const emailRegex = /^\w+([.-]?\w+)*@\w+([.-]?\w+)*(\.\w{2,3})+$/;
if (!credentials.email.trim()) {
newErrors.email = 'Email is required';
} else if (!emailRegex.test(credentials.email)) {
newErrors.email = 'Invalid email address';
}

if (!credentials.password) {
newErrors.password = 'Password is required';
} else if (credentials.password.length < 6) {
newErrors.password = 'Password must be at least 6 characters';
} else {
if (credentials.password.length < 8) newErrors.password = 'Password must be at least 8 characters';
else if (!/[A-Z]/.test(credentials.password)) newErrors.password = 'Password must contain at least one uppercase letter';
else if (!/[0-9]/.test(credentials.password)) newErrors.password = 'Password must contain at least one number';
else if (!/[!@#$%^&*]/.test(credentials.password)) newErrors.password = 'Password must contain at least one special character (!@#$%^&*)';
}

if (!credentials.confirmPassword) {
Expand All @@ -60,6 +68,7 @@ const SignupForm = () => {
newErrors.confirmPassword = 'Passwords do not match';
}


// If validation errors exist, show them and return
if (Object.keys(newErrors).length > 0) {
setErrors(newErrors);
Expand All @@ -69,67 +78,28 @@ const SignupForm = () => {
setIsLoading(true);

try {
// Prepare data for registration
const userData = {
username: credentials.username,
password: credentials.password,
email: credentials.email,
name: credentials.name,
course: credentials.course,
subjectsOfInterest: credentials.subjectsOfInterest.split(',').map(s => s.trim()).filter(s => s)
};

// Use the register method from AuthContext
const result = await register(
credentials.username,
credentials.password
);
const result = await register(userData);

if (result.success) {
toast.success("Account created successfully! 🎉");
router.push("/login")
// The useEffect will handle the redirect
} else {
toast.error(result.message || "Registration failed");
}
} catch (error) {
console.error("Registration error:", error);

// Attempt to extract meaningful error message
let errorMessage = "Something went wrong. Please try again.";

try {
// Check if response exists and extract message from JSON
if (error.response?.data) {
const data = error.response.data;

// Try to get message from JSON response
if (typeof data === 'object' && data !== null) {
if (data.message) {
errorMessage = data.message;
} else if (data.error) {
errorMessage = data.error;
}
}
// If data is a string (HTML), it means we got an error page
else if (typeof data === 'string' && data.includes('<')) {
// HTML response detected, use status code instead
throw new Error('HTML_RESPONSE');
}
}

// Handle HTTP status codes if no JSON message was found
if (errorMessage.includes('Something went wrong')) {
if (error.response?.status === 500) {
errorMessage = "Server error. Please try again later.";
} else if (error.response?.status === 400) {
errorMessage = "Invalid registration details. Please check your input.";
}
}
} catch (parseError) {
// If JSON parsing failed or HTML was detected, use status-based message
if (error.response?.status === 500) {
errorMessage = "Server error. Please try again later.";
} else if (error.response?.status === 400) {
errorMessage = "Invalid registration details. Please check your input.";
} else if (error.response?.status) {
errorMessage = `Registration failed with error ${error.response.status}. Please try again.`;
} else if (error.message && !error.message.includes('<!DOCTYPE')) {
errorMessage = error.message;
}
}

toast.error(errorMessage);
toast.error("Something went wrong. Please try again.");
} finally {
setIsLoading(false);
}
Expand Down Expand Up @@ -176,11 +146,26 @@ const SignupForm = () => {
)}
</div>

{/* Password Field */}

<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
{/* Course */}
<div>
<label className="block text-sm font-medium text-gray-300 mb-1">Course</label>
<input type="text" name="course" value={credentials.course} onChange={onchange} disabled={isLoading}
className="w-full px-4 py-3 border border-gray-600 rounded-lg focus:ring-2 focus:border-blue-500 bg-gray-700 text-white" placeholder="B.Tech, B.Sc, etc." />
</div>

{/* Subjects of Interest */}
<div>
<label className="block text-sm font-medium text-gray-300 mb-1">Interests</label>
<input type="text" name="subjectsOfInterest" value={credentials.subjectsOfInterest} onChange={onchange} disabled={isLoading}
className="w-full px-4 py-3 border border-gray-600 rounded-lg focus:ring-2 focus:border-blue-500 bg-gray-700 text-white" placeholder="Coding, Design (comma separated)" />
</div>
</div>

{/* Password */}
<div>
<label htmlFor="password" className="block text-sm font-medium text-gray-300 mb-2">
Password
</label>
<label className="block text-sm font-medium text-gray-300 mb-1">Password *</label>
<div className="relative">
<input
type={showPassword ? "text" : "password"}
Expand Down Expand Up @@ -220,11 +205,9 @@ const SignupForm = () => {
)}
</div>

{/* Confirm Password Field */}
{/* Confirm Password */}
<div>
<label htmlFor="confirmPassword" className="block text-sm font-medium text-gray-300 mb-2">
Confirm Password
</label>
<label className="block text-sm font-medium text-gray-300 mb-1">Confirm Password *</label>
<div className="relative">
<input
type={showConfirmPassword ? "text" : "password"}
Expand Down Expand Up @@ -280,7 +263,7 @@ const SignupForm = () => {
</button>

{/* Login link */}
<div className="text-center">
<div className="text-center mt-4">
<span className="text-sm text-gray-300">
Already have an account?{' '}
<Link
Expand Down Expand Up @@ -337,7 +320,6 @@ const SignupFormSkeleton = () => {
export default function Signup() {
const { isAuthenticated } = useAuth();

// Show loading while checking initial auth state
if (isAuthenticated === undefined) {
return (
<div className="min-h-screen bg-gradient-to-br from-gray-900 to-blue-900 flex items-center justify-center">
Expand All @@ -360,8 +342,7 @@ export default function Signup() {

return (
<div className="min-h-screen bg-gradient-to-br from-gray-900 to-blue-900 flex items-center justify-center p-4">
<div className="max-w-md w-full">
{/* Header */}
<div className="max-w-2xl w-full">
<div className="text-center mb-8">
<div className="mx-auto h-12 w-12 bg-gradient-to-r from-green-500 to-blue-600 rounded-full flex items-center justify-center mb-4" aria-hidden="true">
<svg className="h-6 w-6 text-white" fill="none" viewBox="0 0 24 24" stroke="currentColor">
Expand All @@ -375,8 +356,6 @@ export default function Signup() {
Join us and get started
</p>
</div>

{/* Signup Form */}
<div className="bg-gray-800 p-8 rounded-2xl shadow-xl border border-gray-700">
<SignupForm />
</div>
Expand Down
Loading