diff --git a/keep-notes/app/layout.tsx b/keep-notes/app/layout.tsx
index c210182..454d294 100644
--- a/keep-notes/app/layout.tsx
+++ b/keep-notes/app/layout.tsx
@@ -2,6 +2,7 @@ import type { Metadata } from "next";
import { Inter } from "next/font/google";
import "./globals.css";
import { Header } from "@/components/header";
+import { ToastProvider } from "@/components/ui/toast";
const inter = Inter({
subsets: ["latin"],
@@ -20,8 +21,10 @@ export default function RootLayout({
return (
-
- {children}
+
+
+ {children}
+
);
diff --git a/keep-notes/components/note-input.tsx b/keep-notes/components/note-input.tsx
index 2c8a44d..286ab71 100644
--- a/keep-notes/components/note-input.tsx
+++ b/keep-notes/components/note-input.tsx
@@ -33,7 +33,7 @@ import {
DropdownMenuTrigger,
} from '@/components/ui/dropdown-menu'
import { cn } from '@/lib/utils'
-import { useUndoRedo } from '@/hooks/useUndoRedo'
+import { useToast } from '@/components/ui/toast'
interface NoteState {
title: string
@@ -43,6 +43,7 @@ interface NoteState {
}
export function NoteInput() {
+ const { addToast } = useToast()
const [isExpanded, setIsExpanded] = useState(false)
const [type, setType] = useState<'text' | 'checklist'>('text')
const [isSubmitting, setIsSubmitting] = useState(false)
@@ -50,92 +51,11 @@ export function NoteInput() {
const [isArchived, setIsArchived] = useState(false)
const fileInputRef = useRef(null)
- // Undo/Redo state management
- const {
- state: noteState,
- setState: setNoteState,
- undo,
- redo,
- canUndo,
- canRedo,
- clear: clearHistory
- } = useUndoRedo({
- title: '',
- content: '',
- checkItems: [],
- images: []
- })
-
- const { title, content, checkItems, images } = noteState
-
- // Debounced state updates for performance
- const debounceTimerRef = useRef(null)
-
- const updateTitle = (newTitle: string) => {
- // Clear previous timer
- if (debounceTimerRef.current) {
- clearTimeout(debounceTimerRef.current)
- }
-
- // Update immediately for UI
- setNoteState(prev => ({ ...prev, title: newTitle }))
-
- // Debounce history update
- debounceTimerRef.current = setTimeout(() => {
- setNoteState(prev => ({ ...prev, title: newTitle }))
- }, 500)
- }
-
- const updateContent = (newContent: string) => {
- // Clear previous timer
- if (debounceTimerRef.current) {
- clearTimeout(debounceTimerRef.current)
- }
-
- // Update immediately for UI
- setNoteState(prev => ({ ...prev, content: newContent }))
-
- // Debounce history update
- debounceTimerRef.current = setTimeout(() => {
- setNoteState(prev => ({ ...prev, content: newContent }))
- }, 500)
- }
-
- const updateCheckItems = (newCheckItems: CheckItem[]) => {
- setNoteState(prev => ({ ...prev, checkItems: newCheckItems }))
- }
-
- const updateImages = (newImages: string[]) => {
- setNoteState(prev => ({ ...prev, images: newImages }))
- }
-
- // Cleanup debounce timer
- useEffect(() => {
- return () => {
- if (debounceTimerRef.current) {
- clearTimeout(debounceTimerRef.current)
- }
- }
- }, [])
-
- // Keyboard shortcuts
- useEffect(() => {
- const handleKeyDown = (e: KeyboardEvent) => {
- if (!isExpanded) return
-
- if ((e.ctrlKey || e.metaKey) && e.key === 'z') {
- e.preventDefault()
- undo()
- }
- if ((e.ctrlKey || e.metaKey) && e.key === 'y') {
- e.preventDefault()
- redo()
- }
- }
-
- window.addEventListener('keydown', handleKeyDown)
- return () => window.removeEventListener('keydown', handleKeyDown)
- }, [isExpanded, undo, redo])
+ // Simple state without complex undo/redo - like Google Keep
+ const [title, setTitle] = useState('')
+ const [content, setContent] = useState('')
+ const [checkItems, setCheckItems] = useState([])
+ const [images, setImages] = useState([])
const handleImageUpload = (e: React.ChangeEvent) => {
const files = e.target.files
@@ -148,21 +68,21 @@ export function NoteInput() {
Array.from(files).forEach(file => {
// Validation
if (!validTypes.includes(file.type)) {
- alert(`Invalid file type: ${file.name}. Only JPEG, PNG, GIF, and WebP are allowed.`)
+ addToast(`Invalid file type: ${file.name}. Only JPEG, PNG, GIF, and WebP allowed.`, 'error')
return
}
if (file.size > maxSize) {
- alert(`File too large: ${file.name}. Maximum size is 5MB.`)
+ addToast(`File too large: ${file.name}. Maximum size is 5MB.`, 'error')
return
}
const reader = new FileReader()
reader.onloadend = () => {
- updateImages([...images, reader.result as string])
+ setImages([...images, reader.result as string])
}
reader.onerror = () => {
- alert(`Failed to read file: ${file.name}`)
+ addToast(`Failed to read file: ${file.name}`, 'error')
}
reader.readAsDataURL(file)
})
@@ -178,34 +98,34 @@ export function NoteInput() {
try {
const date = new Date(reminderDate)
if (isNaN(date.getTime())) {
- alert('Invalid date format. Please use format: YYYY-MM-DD HH:MM')
+ addToast('Invalid date format. Use: YYYY-MM-DD HH:MM', 'error')
return
}
if (date < new Date()) {
- alert('Reminder date must be in the future')
+ addToast('Reminder date must be in the future', 'error')
return
}
- // TODO: Store reminder in database and implement notification system
- alert(`Reminder set for: ${date.toLocaleString()}\n\nNote: Reminder system will be fully implemented in the next update.`)
+ // TODO: Store reminder in database
+ addToast(`Reminder set for: ${date.toLocaleString()}`, 'success')
} catch (error) {
- alert('Failed to set reminder. Please try again.')
+ addToast('Failed to set reminder', 'error')
}
}
const handleSubmit = async () => {
// Validation
if (type === 'text' && !content.trim()) {
- alert('Please enter some content for your note')
+ addToast('Please enter some content', 'warning')
return
}
if (type === 'checklist' && checkItems.length === 0) {
- alert('Please add at least one item to your checklist')
+ addToast('Please add at least one item', 'warning')
return
}
if (type === 'checklist' && checkItems.every(item => !item.text.trim())) {
- alert('Checklist items cannot be empty')
+ addToast('Checklist items cannot be empty', 'warning')
return
}
@@ -221,52 +141,47 @@ export function NoteInput() {
images: images.length > 0 ? images : undefined,
})
- // Reset form and history
- setNoteState({
- title: '',
- content: '',
- checkItems: [],
- images: []
- })
- clearHistory()
+ // Reset form
+ setTitle('')
+ setContent('')
+ setCheckItems([])
+ setImages([])
setIsExpanded(false)
setType('text')
setColor('default')
setIsArchived(false)
+
+ addToast('Note created successfully', 'success')
} catch (error) {
console.error('Failed to create note:', error)
- alert('Failed to create note. Please try again.')
+ addToast('Failed to create note', 'error')
} finally {
setIsSubmitting(false)
}
}
const handleAddCheckItem = () => {
- updateCheckItems([
+ setCheckItems([
...checkItems,
{ id: Date.now().toString(), text: '', checked: false },
])
}
-
const handleUpdateCheckItem = (id: string, text: string) => {
- updateCheckItems(
+ setCheckItems(
checkItems.map(item => (item.id === id ? { ...item, text } : item))
)
}
const handleRemoveCheckItem = (id: string) => {
- updateCheckItems(checkItems.filter(item => item.id !== id))
+ setCheckItems(checkItems.filter(item => item.id !== id))
}
const handleClose = () => {
setIsExpanded(false)
- setNoteState({
- title: '',
- content: '',
- checkItems: [],
- images: []
- })
- clearHistory()
+ setTitle('')
+ setContent('')
+ setCheckItems([])
+ setImages([])
setType('text')
setColor('default')
setIsArchived(false)
@@ -310,7 +225,7 @@ export function NoteInput() {
updateTitle(e.target.value)}
+ onChange={(e) => setTitle(e.target.value)}
className="border-0 focus-visible:ring-0 text-base font-semibold"
/>
@@ -328,7 +243,7 @@ export function NoteInput() {
variant="ghost"
size="sm"
className="absolute top-2 right-2 h-7 w-7 p-0 bg-white/90 hover:bg-white opacity-0 group-hover:opacity-100 transition-opacity"
- onClick={() => updateImages(images.filter((_, i) => i !== idx))}
+ onClick={() => setImages(images.filter((_, i) => i !== idx))}
>
@@ -341,7 +256,7 @@ export function NoteInput() {