linknotemaster-pro / notes.html
Jdifb's picture
build me a app where I can write notes store links. like a note app but with more features add more features
43c33a1 verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>LinkNoteMaster Pro - Notes</title>
<script src="https://cdn.tailwindcss.com"></script>
<script src="https://unpkg.com/feather-icons"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
</head>
<body class="bg-gray-50 min-h-screen">
<!-- Header -->
<header class="bg-white shadow-sm border-b">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div class="flex justify-between items-center py-4">
<div class="flex items-center space-x-4">
<a href="index.html" class="text-gray-600 hover:text-gray-900">
<i data-feather="arrow-left"></i>
</a>
<h1 class="text-2xl font-bold text-gray-900">LinkNoteMaster Pro</h1>
</div>
<button id="newNoteBtn" class="bg-blue-600 hover:bg-blue-700 text-white px-4 py-2 rounded-lg flex items-center space-x-2 transition-colors">
<i data-feather="plus"></i>
<span>New Note</span>
</button>
</div>
</div>
</header>
<!-- Main Content -->
<main class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
<!-- Search and Filter -->
<div class="mb-8">
<div class="flex flex-col sm:flex-row gap-4">
<div class="flex-1">
<div class="relative">
<input type="text" id="searchInput" placeholder="Search notes and links..." class="w-full pl-10 pr-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent">
<i data-feather="search" class="absolute left-3 top-3.5 text-gray-400"></i>
</div>
</div>
<div class="flex space-x-2">
<select id="filterSelect" class="border border-gray-300 rounded-lg px-4 py-3 focus:ring-2 focus:ring-blue-500 focus:border-transparent">
<option value="all">All Notes</option>
<option value="text">Text Only</option>
<option value="link">Links Only</option>
<option value="favorite">Favorites</option>
</select>
</div>
</div>
</div>
<!-- Notes Grid -->
<div id="notesContainer" class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-6">
<!-- Notes will be dynamically added here -->
</div>
<!-- Empty State -->
<div id="emptyState" class="text-center py-16 hidden">
<i data-feather="file-text" class="w-16 h-16 text-gray-300 mx-auto mb-4"></i>
<h3 class="text-lg font-medium text-gray-900 mb-2">No notes yet</h3>
<p class="text-gray-500 mb-6">Create your first note to get started!</p>
<button id="emptyNewNoteBtn" class="bg-blue-600 hover:bg-blue-700 text-white px-6 py-3 rounded-lg transition-colors">
Create Your First Note
</button>
</div>
</main>
<!-- Add/Edit Note Modal -->
<div id="noteModal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center p-4 z-50 hidden">
<div class="bg-white rounded-xl shadow-2xl max-w-2xl w-full max-h-[90vh] overflow-hidden">
<div class="flex justify-between items-center p-6 border-b">
<h3 class="text-xl font-semibold" id="modalTitle">New Note</h3>
<button id="closeModal" class="text-gray-400 hover:text-gray-600">
<i data-feather="x"></i>
</button>
</div>
<div class="p-6">
<form id="noteForm">
<div class="space-y-4">
<div>
<label class="block text-sm font-medium text-gray-700 mb-2">Title</label>
<input type="text" id="noteTitle" class="w-full border border-gray-300 rounded-lg px-4 py-3 focus:ring-2 focus:ring-blue-500 focus:border-transparent" placeholder="Enter note title" required>
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-2">Content</label>
<textarea id="noteContent" rows="6" class="w-full border border-gray-300 rounded-lg px-4 py-3 focus:ring-2 focus:ring-blue-500 focus:border-transparent" placeholder="Enter your note content..."></textarea>
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-2">Link (Optional)</label>
<input type="url" id="noteLink" class="w-full border border-gray-300 rounded-lg px-4 py-3 focus:ring-2 focus:ring-blue-500 focus:border-transparent" placeholder="https://example.com">
</div>
<div class="flex items-center justify-between">
<div class="flex items-center space-x-4">
<label class="flex items-center">
<input type="checkbox" id="noteFavorite" class="rounded border-gray-300 text-blue-600 focus:ring-blue-500">
<span class="ml-2 text-sm text-gray-700">Mark as favorite</span>
</label>
<label class="flex items-center">
<input type="checkbox" id="notePrivate" class="rounded border-gray-300 text-blue-600 focus:ring-blue-500">
<span class="ml-2 text-sm text-gray-700">Private note</span>
</label>
</div>
<div class="flex space-x-3">
<button type="button" id="cancelNote" class="px-4 py-2 border border-gray-300 rounded-lg text-gray-700 hover:bg-gray-50 transition-colors">
Cancel
</button>
<button type="submit" class="bg-blue-600 hover:bg-blue-700 text-white px-6 py-2 rounded-lg transition-colors">
Save Note
</button>
</div>
</div>
</div>
</form>
</div>
</div>
</div>
<script>
// Notes data structure
let notes = JSON.parse(localStorage.getItem('notes')) || [];
let currentEditingId = null;
// DOM Elements
const notesContainer = document.getElementById('notesContainer');
const emptyState = document.getElementById('emptyState');
const noteModal = document.getElementById('noteModal');
const noteForm = document.getElementById('noteForm');
const searchInput = document.getElementById('searchInput');
const filterSelect = document.getElementById('filterSelect');
// Initialize
document.addEventListener('DOMContentLoaded', function() {
feather.replace();
renderNotes();
setupEventListeners();
});
function setupEventListeners() {
// New note buttons
document.getElementById('newNoteBtn').addEventListener('click', () => openModal());
document.getElementById('emptyNewNoteBtn').addEventListener('click', () => openModal());
// Modal controls
document.getElementById('closeModal').addEventListener('click', () => closeModal());
document.getElementById('cancelNote').addEventListener('click', () => closeModal());
// Form submission
noteForm.addEventListener('submit', handleNoteSubmit);
// Search and filter
searchInput.addEventListener('input', renderNotes);
filterSelect.addEventListener('change', renderNotes);
}
function openModal(note = null) {
currentEditingId = note ? note.id : null;
document.getElementById('modalTitle').textContent = note ? 'Edit Note' : 'New Note';
document.getElementById('noteTitle').value = note ? note.title : '';
document.getElementById('noteContent').value = note ? note.content : '';
document.getElementById('noteLink').value = note ? note.link || '' : '';
document.getElementById('noteFavorite').checked = note ? note.favorite : false;
document.getElementById('notePrivate').checked = note ? note.private : false;
noteModal.classList.remove('hidden');
}
function closeModal() {
noteModal.classList.add('hidden');
currentEditingId = null;
noteForm.reset();
}
function handleNoteSubmit(e) {
e.preventDefault();
const noteData = {
id: currentEditingId || Date.now().toString(),
title: document.getElementById('noteTitle').value,
content: document.getElementById('noteContent').value,
link: document.getElementById('noteLink').value,
favorite: document.getElementById('noteFavorite').checked,
private: document.getElementById('notePrivate').checked,
createdAt: currentEditingId ? notes.find(n => n.id === currentEditingId).createdAt : new Date().toISOString(),
updatedAt: new Date().toISOString()
};
if (currentEditingId) {
const index = notes.findIndex(n => n.id === currentEditingId);
notes[index] = noteData;
} else {
notes.unshift(noteData);
}
saveNotes();
renderNotes();
closeModal();
}
function saveNotes() {
localStorage.setItem('notes', JSON.stringify(notes));
}
function renderNotes() {
const searchTerm = searchInput.value.toLowerCase();
const filter = filterSelect.value;
let filteredNotes = notes.filter(note => {
const matchesSearch = note.title.toLowerCase().includes(searchTerm) ||
note.content.toLowerCase().includes(searchTerm) ||
(note.link && note.link.toLowerCase().includes(searchTerm));
if (filter === 'text') return matchesSearch && !note.link;
if (filter === 'link') return matchesSearch && note.link;
if (filter === 'favorite') return matchesSearch && note.favorite;
return matchesSearch;
});
if (filteredNotes.length === 0) {
notesContainer.classList.add('hidden');
emptyState.classList.remove('hidden');
return;
}
notesContainer.classList.remove('hidden');
emptyState.classList.add('hidden');
notesContainer.innerHTML = filteredNotes.map(note => `
<div class="bg-white rounded-xl shadow-sm border border-gray-200 hover:shadow-md transition-shadow overflow-hidden">
<div class="p-6">
<div class="flex justify-between items-start mb-3">
<h3 class="font-semibold text-gray-900 text-lg truncate">${note.title}</h3>
<div class="flex space-x-1">
${note.favorite ? '<i data-feather="star" class="w-4 h-4 text-yellow-500 fill-current"></i>' : ''}
${note.private ? '<i data-feather="lock" class="w-4 h-4 text-gray-400"></i>' : ''}
</div>
</div>
<p class="text-gray-600 text-sm mb-4 line-clamp-3">${note.content}</p>
${note.link ? `
<div class="mb-4">
<a href="${note.link}" target="_blank" class="text-blue-600 hover:text-blue-800 text-sm truncate block">
<i data-feather="link" class="w-3 h-3 inline mr-1"></i>
${note.link}
</a>
</div>
` : ''}
<div class="flex justify-between items-center text-xs text-gray-500">
<span>${new Date(note.updatedAt).toLocaleDateString()}</span>
<div class="flex space-x-2">
<button onclick="editNote('${note.id}')" class="text-gray-400 hover:text-blue-600 transition-colors">
<i data-feather="edit-2" class="w-4 h-4"></i>
</button>
<button onclick="deleteNote('${note.id}')" class="text-gray-400 hover:text-red-600 transition-colors">
<i data-feather="trash-2" class="w-4 h-4"></i>
</button>
</div>
</div>
</div>
</div>
`).join('');
feather.replace();
}
function editNote(id) {
const note = notes.find(n => n.id === id);
if (note) openModal(note);
}
function deleteNote(id) {
if (confirm('Are you sure you want to delete this note?')) {
notes = notes.filter(note => note.id !== id);
saveNotes();
renderNotes();
}
}
// Make functions globally available for onclick handlers
window.editNote = editNote;
window.deleteNote = deleteNote;
</script>
</body>
</html>