diff --git a/savebook/app/(legal)/privacy/page.js b/savebook/app/(legal)/privacy/page.js index 0e8dea5..8a8ebf7 100644 --- a/savebook/app/(legal)/privacy/page.js +++ b/savebook/app/(legal)/privacy/page.js @@ -397,7 +397,7 @@ const PrivacyPolicyContent = () => { Policy Updates

- We may update this Privacy Policy from time to time. We will notify you of any changes by posting the new Privacy Policy on this page and updating the "Last Updated" date. You are advised to review this Privacy Policy periodically for any changes. + We may update this Privacy Policy from time to time. We will notify you of any changes by posting the new Privacy Policy on this page and updating the "Last Updated" date. You are advised to review this Privacy Policy periodically for any changes.

diff --git a/savebook/app/(legal)/terms/page.js b/savebook/app/(legal)/terms/page.js index 4ada2e8..1ec3bd3 100644 --- a/savebook/app/(legal)/terms/page.js +++ b/savebook/app/(legal)/terms/page.js @@ -150,7 +150,7 @@ const TermsOfServiceContent = () => {

- By accessing or using SaveBook ("the Service"), you agree to be bound by these Terms of Service and all applicable laws and regulations. If you do not agree with any of these terms, you are prohibited from using or accessing this Service. + By accessing or using SaveBook ("the Service"), you agree to be bound by these Terms of Service and all applicable laws and regulations. If you do not agree with any of these terms, you are prohibited from using or accessing this Service.

diff --git a/savebook/app/contact/page.js b/savebook/app/contact/page.js index 64d4c81..d046e30 100644 --- a/savebook/app/contact/page.js +++ b/savebook/app/contact/page.js @@ -66,7 +66,7 @@ const ContactPage = () => { Contact Us

- Have questions or feedback? We'd love to hear from you. Send us a message and we'll respond as soon as possible. + Have questions or feedback? We'd love to hear from you. Send us a message and we'll respond as soon as possible.

{/* Alert (replaces success toast) */} diff --git a/savebook/app/not-found.js b/savebook/app/not-found.js index 20e1e55..5251266 100644 --- a/savebook/app/not-found.js +++ b/savebook/app/not-found.js @@ -11,7 +11,7 @@ const NotFoundContent = () => {

Page not found

- The page you're looking for doesn't exist or has been moved. + The page you're looking for doesn't exist or has been moved.

{/* Simple action button */} diff --git a/savebook/app/page.js b/savebook/app/page.js index 1463104..3423946 100644 --- a/savebook/app/page.js +++ b/savebook/app/page.js @@ -163,7 +163,7 @@ export default function Home() { className="mt-8 p-4 bg-green-50 dark:bg-green-900/20 border border-green-200 dark:border-green-800 rounded-xl" >

- ✨ You're all set! Start organizing your thoughts and ideas. + ✨ You're all set! Start organizing your thoughts and ideas.

)} diff --git a/savebook/components/notes/AddNote.js b/savebook/components/notes/AddNote.js index bde454c..029fb0b 100644 --- a/savebook/components/notes/AddNote.js +++ b/savebook/components/notes/AddNote.js @@ -1,616 +1,146 @@ -"use client" -import noteContext from '@/context/noteContext'; -import React, { useContext, useState } from 'react' -import toast from 'react-hot-toast'; -import Modal from '../common/Modal'; -import AudioRecorder from '@/components/AudioRecorder'; -import AudioPlayer from '@/components/AudioPlayer'; +"use client"; -// Define Note Templates +import React, { useContext, useState } from "react"; +import noteContext from "@/context/noteContext"; +import toast from "react-hot-toast"; +import { encrypt } from "@/lib/utils/crypto"; + +/* Note templates */ const NOTE_TEMPLATES = { - meeting: `Date: [Insert Date]\n\nAttendees: [List attendees]\n\nAgenda:\n- \n- \n- \n\nNotes:\n\nAction Items:\n- \n- `, - journal: `What happened today:\n[Write your experiences here]\n\nGoals for tomorrow:\n[List your goals]\n\nGratitude:\n[Write things you're grateful for]`, - checklist: `Project: [Project Name]\n\nTasks:\n- [ ] Define project goal\n- [ ] List all tasks\n- [ ] Assign owners\n- [ ] Set deadlines\n- [ ] Plan resources\n- [ ] Review progress\n- [ ] Complete project` + meeting: `Date:\n\nAttendees:\n\nAgenda:\n- \n- \n\nNotes:\n\nAction Items:\n- `, + journal: `What happened today:\n\nGoals for tomorrow:\n\nGratitude:\n`, + checklist: `Tasks:\n- [ ] Task 1\n- [ ] Task 2\n- [ ] Task 3`, }; -export default function Addnote() { - const context = useContext(noteContext); - const { addNote, notes } = context; - - const [note, setNote] = useState({ title: "", description: "", tag: "" }); - const [isSubmitting, setIsSubmitting] = useState(false); - - // Modal State - const [showModal, setShowModal] = useState(false); - const [pendingTemplate, setPendingTemplate] = useState(null); - const defaultTags = [ - "General", - "Basic", - "Finance", - "Grocery", - "Office", - "Personal", - "Work", - "Ideas" - ]; - const [images, setImages] = useState([]); - const [preview, setPreview] = useState([]); - - // Audio state management - const [audioBlob, setAudioBlob] = useState(null); - const [recordedAudioUrl, setRecordedAudioUrl] = useState(null); - const [audioData, setAudioData] = useState(null); - const [isUploadingAudio, setIsUploadingAudio] = useState(false); - - const handleImageChange = (e) => { - const files = Array.from(e.target.files); - - setImages(files); - setPreview(files.map(file => URL.createObjectURL(file))); - }; - - - const uploadImages = async () => { - const formData = new FormData(); - images.forEach((file) => formData.append("image", file)); - - const res = await fetch("/api/upload/user-media", { - method: "POST", - credentials: "include", - body: formData, - }); - - if (!res.ok) { - throw new Error("Image upload failed"); - } - - const data = await res.json(); - return Array.isArray(data.imageUrls) ? data.imageUrls : []; - }; - - // Handle audio recording from AudioRecorder component - const handleAudioRecorded = (blob) => { - setAudioBlob(blob); - // Create temporary URL for preview - const url = URL.createObjectURL(blob); - setRecordedAudioUrl(url); - }; - - // Upload audio to API - const uploadAudio = async (blob) => { - const formData = new FormData(); - formData.append('audio', blob, 'recording.webm'); - - const res = await fetch('/api/upload/audio', { - method: 'POST', - credentials: 'include', - body: formData, - }); - - if (!res.ok) { - // Log full error response for debugging - let errorMessage = `HTTP ${res.status}`; - const contentType = res.headers.get('content-type'); - - try { - // Try to parse as JSON first - if (contentType?.includes('application/json')) { - const errorData = await res.json(); - errorMessage = errorData.error || errorData.message || errorMessage; - } else { - // Fallback to text - const errorText = await res.text(); - errorMessage = errorText.slice(0, 200) || errorMessage; - } - } catch (parseError) { - // If parsing fails, use status code - console.error('Failed to parse error response:', parseError); - } - - console.error('Audio upload error:', { status: res.status, error: errorMessage }); - throw new Error(`Audio upload failed: ${errorMessage}`); - } - - const data = await res.json(); - return { - url: data.audioUrl, - duration: data.duration || 0, - }; - }; - - // Clear audio recording - const clearAudioRecording = () => { - if (recordedAudioUrl) { - URL.revokeObjectURL(recordedAudioUrl); - } - setAudioBlob(null); - setRecordedAudioUrl(null); - setAudioData(null); - }; - - - - - - - - const handleSaveNote = async (e) => { - e.preventDefault(); - if (isSubmitting) return; - - setIsSubmitting(true); - try { - // Upload images - const imageUrls = images.length ? await uploadImages() : []; - - // Upload audio if recording exists - REQUIRED before note creation - let finalAudioData = null; - if (audioBlob) { - setIsUploadingAudio(true); - try { - finalAudioData = await uploadAudio(audioBlob); - } catch (audioError) { - console.error('Audio upload error:', audioError); - toast.error(audioError.message || 'Audio upload failed. Please try again.'); - // Abort note creation - do NOT save note without audio - return; - } - } - - // Save note with audio data (if available) - await addNote( - note.title, - note.description, - note.tag, - imageUrls, - finalAudioData // Pass audio data (or null) - ); - - toast.success("Note has been saved"); - setNote({ title: "", description: "", tag: "" }); - setImages([]); - setPreview([]); - clearAudioRecording(); // Only clear after successful save - } catch (error) { - console.error('Error saving note:', error); - toast.error(error.message || "Failed to save note. Please try again."); - } finally { - setIsSubmitting(false); - setIsUploadingAudio(false); - } - }; - - - - - - const onchange = (e) => { - setNote({ ...note, [e.target.name]: e.target.value }); - } - - // Apply Template Handler - const applyTemplate = (templateKey) => { - const template = NOTE_TEMPLATES[templateKey]; - if (!template) return; - - // If description is empty, directly apply the template - if (!note.description.trim()) { - setNote({ ...note, description: template }); - toast.success(`${templateKey.charAt(0).toUpperCase() + templateKey.slice(1)} template applied!`); - } else { - // If description has content, trigger modal - setPendingTemplate({ key: templateKey, content: template }); - setShowModal(true); - } - } - - const confirmTemplateChange = () => { - if (pendingTemplate) { - setNote({ ...note, description: pendingTemplate.content }); - toast.success(`${pendingTemplate.key.charAt(0).toUpperCase() + pendingTemplate.key.slice(1)} template applied!`); - handleCloseModal(); - } +export default function AddNote() { + const { addNote, notes } = useContext(noteContext); + + const [note, setNote] = useState({ title: "", description: "", tag: "" }); + const [isSubmitting, setIsSubmitting] = useState(false); + + const defaultTags = [ + "General", + "Basic", + "Finance", + "Grocery", + "Office", + "Personal", + "Work", + "Ideas", + ]; + + const handleChange = (e) => { + setNote({ ...note, [e.target.name]: e.target.value }); + }; + + const handleSaveNote = async (e) => { + e.preventDefault(); + if (isSubmitting) return; + + setIsSubmitting(true); + + try { + const secret = localStorage.getItem("encryptionKey"); + if (!secret) { + toast.error("Encryption key missing. Please log in again."); + return; + } + + const encryptedTitle = encrypt(note.title, secret); + const encryptedDescription = encrypt(note.description, secret); + + await addNote(encryptedTitle, encryptedDescription, note.tag); + + toast.success("Note saved"); + setNote({ title: "", description: "", tag: "" }); + } catch (err) { + toast.error("Failed to save note"); + } finally { + setIsSubmitting(false); } + }; - const handleCloseModal = () => { - setShowModal(false); - setPendingTemplate(null); + const applyTemplate = (key) => { + if (NOTE_TEMPLATES[key]) { + setNote({ ...note, description: NOTE_TEMPLATES[key] }); + toast.success("Template applied"); } - // Collect unique tags from existing notes - const userTags = Array.from( - new Set( - (Array.isArray(notes) ? notes : []) - .map(note => note.tag) - .filter(tag => tag && tag.trim() !== "") - ) - ); - - const allTags = Array.from(new Set([...defaultTags, ...userTags])); - const isFormValid = note.title.length >= 5 && note.description.length >= 5 && note.tag.length >= 2; - - return ( -
-
- {/* Header */} -
-

- Notebook on the Cloud -

-

- Your notes, securely stored and accessible anywhere -

-
- - {/* Add Note Form */} -
-

- Add New Note -

- -
- {/* Title Field */} -
- - -

- Minimum 5 characters required -

-
- - {/* Description Field with Templates */} -
-
- - - Quick Templates: - -
- - {/* Templates Row - Quick Start Buttons */} -
- - - - - -
- -