| | <!DOCTYPE html> |
| | <html lang="ar" dir="rtl"> |
| | <head> |
| | <meta charset="UTF-8"> |
| | <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| | <title>ملاحظاتي | تطبيق الملاحظات الشخصية</title> |
| | |
| | <link href="https://fonts.googleapis.com/css2?family=Cairo:wght@300;400;600;700&display=swap" rel="stylesheet"> |
| | <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> |
| | |
| | <style> |
| | |
| | |
| | |
| | |
| | :root { |
| | --primary-color: #6c5ce7; |
| | --primary-hover: #5649c0; |
| | --bg-color: #f3f4f6; |
| | --card-bg: #ffffff; |
| | --text-main: #2d3436; |
| | --text-secondary: #636e72; |
| | --danger-color: #ff7675; |
| | --shadow-sm: 0 2px 5px rgba(0,0,0,0.05); |
| | --shadow-md: 0 5px 15px rgba(0,0,0,0.08); |
| | --radius: 16px; |
| | --transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1); |
| | } |
| | |
| | * { |
| | box-sizing: border-box; |
| | margin: 0; |
| | padding: 0; |
| | outline: none; |
| | } |
| | |
| | body { |
| | font-family: 'Cairo', system-ui, -apple-system, sans-serif; |
| | background-color: var(--bg-color); |
| | color: var(--text-main); |
| | line-height: 1.6; |
| | min-height: 100vh; |
| | display: flex; |
| | flex-direction: column; |
| | } |
| | |
| | |
| | header { |
| | background: var(--card-bg); |
| | padding: 1.5rem 2rem; |
| | box-shadow: var(--shadow-sm); |
| | position: sticky; |
| | top: 0; |
| | z-index: 100; |
| | backdrop-filter: blur(10px); |
| | background: rgba(255, 255, 255, 0.9); |
| | } |
| | |
| | .header-content { |
| | max-width: 1200px; |
| | margin: 0 auto; |
| | display: flex; |
| | flex-wrap: wrap; |
| | justify-content: space-between; |
| | align-items: center; |
| | gap: 1rem; |
| | } |
| | |
| | .logo { |
| | font-size: 1.8rem; |
| | font-weight: 700; |
| | color: var(--primary-color); |
| | display: flex; |
| | align-items: center; |
| | gap: 0.5rem; |
| | } |
| | |
| | .search-container { |
| | position: relative; |
| | flex-grow: 1; |
| | max-width: 400px; |
| | } |
| | |
| | .search-input { |
| | width: 100%; |
| | padding: 0.8rem 1rem 0.8rem 2.5rem; |
| | border: 2px solid #e0e0e0; |
| | border-radius: 50px; |
| | font-family: inherit; |
| | font-size: 1rem; |
| | transition: var(--transition); |
| | } |
| | |
| | .search-input:focus { |
| | border-color: var(--primary-color); |
| | box-shadow: 0 0 0 4px rgba(108, 92, 231, 0.1); |
| | } |
| | |
| | .search-icon { |
| | position: absolute; |
| | left: 15px; |
| | top: 50%; |
| | transform: translateY(-50%); |
| | color: var(--text-secondary); |
| | } |
| | |
| | .actions { |
| | display: flex; |
| | gap: 10px; |
| | } |
| | |
| | .btn { |
| | padding: 0.6rem 1.2rem; |
| | border: none; |
| | border-radius: 12px; |
| | cursor: pointer; |
| | font-family: inherit; |
| | font-weight: 600; |
| | display: flex; |
| | align-items: center; |
| | gap: 0.5rem; |
| | transition: var(--transition); |
| | font-size: 0.9rem; |
| | } |
| | |
| | .btn-primary { |
| | background-color: var(--primary-color); |
| | color: white; |
| | box-shadow: 0 4px 10px rgba(108, 92, 231, 0.3); |
| | } |
| | |
| | .btn-primary:hover { |
| | background-color: var(--primary-hover); |
| | transform: translateY(-2px); |
| | } |
| | |
| | .btn-danger { |
| | background-color: #fff0f0; |
| | color: var(--danger-color); |
| | border: 1px solid #ffe0e0; |
| | } |
| | |
| | .btn-danger:hover { |
| | background-color: var(--danger-color); |
| | color: white; |
| | } |
| | |
| | |
| | .container { |
| | max-width: 1200px; |
| | margin: 2rem auto; |
| | padding: 0 1.5rem; |
| | flex-grow: 1; |
| | width: 100%; |
| | } |
| | |
| | .notes-grid { |
| | display: grid; |
| | grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); |
| | gap: 1.5rem; |
| | } |
| | |
| | |
| | .note-card { |
| | background: var(--card-bg); |
| | border-radius: var(--radius); |
| | padding: 1.5rem; |
| | box-shadow: var(--shadow-sm); |
| | transition: var(--transition); |
| | position: relative; |
| | display: flex; |
| | flex-direction: column; |
| | border: 1px solid transparent; |
| | animation: fadeIn 0.4s ease-out forwards; |
| | } |
| | |
| | @keyframes fadeIn { |
| | from { opacity: 0; transform: translateY(20px); } |
| | to { opacity: 1; transform: translateY(0); } |
| | } |
| | |
| | .note-card:hover { |
| | transform: translateY(-5px); |
| | box-shadow: var(--shadow-md); |
| | border-color: rgba(108, 92, 231, 0.1); |
| | } |
| | |
| | .note-header { |
| | display: flex; |
| | justify-content: space-between; |
| | align-items: flex-start; |
| | margin-bottom: 1rem; |
| | } |
| | |
| | .note-title { |
| | font-size: 1.2rem; |
| | font-weight: 700; |
| | color: var(--text-main); |
| | word-break: break-word; |
| | } |
| | |
| | .note-date { |
| | font-size: 0.75rem; |
| | color: var(--text-secondary); |
| | background: #f0f0f0; |
| | padding: 0.2rem 0.6rem; |
| | border-radius: 20px; |
| | white-space: nowrap; |
| | } |
| | |
| | .note-content { |
| | color: var(--text-secondary); |
| | font-size: 0.95rem; |
| | margin-bottom: 1.5rem; |
| | white-space: pre-wrap; |
| | display: -webkit-box; |
| | -webkit-line-clamp: 5; |
| | -webkit-box-orient: vertical; |
| | overflow: hidden; |
| | flex-grow: 1; |
| | } |
| | |
| | .note-actions { |
| | display: flex; |
| | justify-content: flex-end; |
| | gap: 0.5rem; |
| | border-top: 1px solid #f0f0f0; |
| | padding-top: 1rem; |
| | } |
| | |
| | .icon-btn { |
| | background: none; |
| | border: none; |
| | cursor: pointer; |
| | font-size: 1rem; |
| | padding: 0.5rem; |
| | border-radius: 8px; |
| | transition: var(--transition); |
| | color: var(--text-secondary); |
| | } |
| | |
| | .icon-btn:hover { |
| | background-color: #f3f4f6; |
| | color: var(--primary-color); |
| | } |
| | |
| | .icon-btn.delete:hover { |
| | color: var(--danger-color); |
| | background-color: #fff0f0; |
| | } |
| | |
| | |
| | .empty-state { |
| | text-align: center; |
| | padding: 4rem 1rem; |
| | color: var(--text-secondary); |
| | display: none; |
| | } |
| | |
| | .empty-state i { |
| | font-size: 4rem; |
| | color: #dfe6e9; |
| | margin-bottom: 1rem; |
| | } |
| | |
| | .empty-state h3 { |
| | font-size: 1.5rem; |
| | margin-bottom: 0.5rem; |
| | color: var(--text-main); |
| | } |
| | |
| | |
| | .modal-overlay { |
| | position: fixed; |
| | top: 0; |
| | left: 0; |
| | width: 100%; |
| | height: 100%; |
| | background: rgba(0, 0, 0, 0.5); |
| | backdrop-filter: blur(5px); |
| | display: flex; |
| | justify-content: center; |
| | align-items: center; |
| | z-index: 1000; |
| | opacity: 0; |
| | visibility: hidden; |
| | transition: var(--transition); |
| | } |
| | |
| | .modal-overlay.active { |
| | opacity: 1; |
| | visibility: visible; |
| | } |
| | |
| | .modal { |
| | background: var(--card-bg); |
| | width: 90%; |
| | max-width: 500px; |
| | border-radius: 20px; |
| | padding: 2rem; |
| | transform: scale(0.9); |
| | transition: var(--transition); |
| | box-shadow: 0 10px 30px rgba(0,0,0,0.2); |
| | } |
| | |
| | .modal-overlay.active .modal { |
| | transform: scale(1); |
| | } |
| | |
| | .modal-header { |
| | display: flex; |
| | justify-content: space-between; |
| | align-items: center; |
| | margin-bottom: 1.5rem; |
| | } |
| | |
| | .modal-title { |
| | font-size: 1.4rem; |
| | font-weight: 700; |
| | } |
| | |
| | .close-modal { |
| | background: none; |
| | border: none; |
| | font-size: 1.5rem; |
| | cursor: pointer; |
| | color: var(--text-secondary); |
| | } |
| | |
| | .form-group { |
| | margin-bottom: 1.2rem; |
| | } |
| | |
| | .form-label { |
| | display: block; |
| | margin-bottom: 0.5rem; |
| | font-weight: 600; |
| | font-size: 0.9rem; |
| | } |
| | |
| | .form-control { |
| | width: 100%; |
| | padding: 0.8rem; |
| | border: 2px solid #e0e0e0; |
| | border-radius: 12px; |
| | font-family: inherit; |
| | font-size: 1rem; |
| | transition: var(--transition); |
| | } |
| | |
| | .form-control:focus { |
| | border-color: var(--primary-color); |
| | } |
| | |
| | textarea.form-control { |
| | resize: vertical; |
| | min-height: 120px; |
| | } |
| | |
| | .modal-footer { |
| | display: flex; |
| | justify-content: flex-end; |
| | gap: 1rem; |
| | margin-top: 1.5rem; |
| | } |
| | |
| | |
| | footer { |
| | text-align: center; |
| | padding: 1.5rem; |
| | color: var(--text-secondary); |
| | font-size: 0.9rem; |
| | margin-top: auto; |
| | } |
| | |
| | .anycoder-link { |
| | color: var(--primary-color); |
| | text-decoration: none; |
| | font-weight: 700; |
| | transition: color 0.2s; |
| | } |
| | |
| | .anycoder-link:hover { |
| | color: var(--primary-hover); |
| | text-decoration: underline; |
| | } |
| | |
| | |
| | @media (max-width: 768px) { |
| | .header-content { |
| | flex-direction: column; |
| | align-items: stretch; |
| | } |
| | |
| | .logo { |
| | justify-content: center; |
| | margin-bottom: 1rem; |
| | } |
| | |
| | .search-container { |
| | max-width: 100%; |
| | } |
| | |
| | .actions { |
| | justify-content: center; |
| | } |
| | |
| | .notes-grid { |
| | grid-template-columns: 1fr; |
| | } |
| | } |
| | </style> |
| | </head> |
| | <body> |
| |
|
| | |
| | <header> |
| | <div class="header-content"> |
| | <div class="logo"> |
| | <i class="fa-solid fa-note-sticky"></i> |
| | ملاحظاتي |
| | </div> |
| | |
| | <div class="search-container"> |
| | <input type="text" id="searchInput" class="search-input" placeholder="ابحث في ملاحظاتك..."> |
| | <i class="fa-solid fa-search search-icon"></i> |
| | </div> |
| |
|
| | <div class="actions"> |
| | <button class="btn btn-danger" onclick="clearAllNotes()"> |
| | <i class="fa-solid fa-trash-can"></i> مسح الكل |
| | </button> |
| | <button class="btn btn-primary" onclick="openModal()"> |
| | <i class="fa-solid fa-plus"></i> ملاحظة جديدة |
| | </button> |
| | </div> |
| | </div> |
| | </header> |
| |
|
| | |
| | <main class="container"> |
| | |
| | <div id="notesGrid" class="notes-grid"> |
| | |
| | </div> |
| |
|
| | |
| | <div id="emptyState" class="empty-state"> |
| | <i class="fa-regular fa-folder-open"></i> |
| | <h3>لا توجد ملاحظات بعد</h3> |
| | <p>ابدأ بإضافة ملاحظة جديدة لتوثيق أفكارك</p> |
| | </div> |
| | </main> |
| |
|
| | |
| | <footer> |
| | <p>تم التطوير بواسطة <a href="https://huggingface.co/spaces/akhaliq/anycoder" target="_blank" class="anycoder-link">Built with anycoder</a></p> |
| | </footer> |
| |
|
| | |
| | <div id="noteModal" class="modal-overlay"> |
| | <div class="modal"> |
| | <div class="modal-header"> |
| | <h2 class="modal-title" id="modalTitleText">ملاحظة جديدة</h2> |
| | <button class="close-modal" onclick="closeModal()">×</button> |
| | </div> |
| | <div class="form-group"> |
| | <label class="form-label">العنوان</label> |
| | <input type="text" id="noteTitle" class="form-control" placeholder="اكتب عنواناً جذاباً..."> |
| | </div> |
| | <div class="form-group"> |
| | <label class="form-label">المحتوى</label> |
| | <textarea id="noteContent" class="form-control" placeholder="اكتب تفاصيل ملاحظتك هنا..."></textarea> |
| | </div> |
| | <div class="modal-footer"> |
| | <button class="btn" style="background: #eee; color: #333;" onclick="closeModal()">إلغاء</button> |
| | <button class="btn btn-primary" onclick="saveNote()">حفظ الملاحظة</button> |
| | </div> |
| | </div> |
| | </div> |
| |
|
| | <script> |
| | |
| | let notes = []; |
| | let isEditing = false; |
| | let currentEditId = null; |
| | |
| | |
| | const notesGrid = document.getElementById('notesGrid'); |
| | const emptyState = document.getElementById('emptyState'); |
| | const modal = document.getElementById('noteModal'); |
| | const modalTitleText = document.getElementById('modalTitleText'); |
| | const titleInput = document.getElementById('noteTitle'); |
| | const contentInput = document.getElementById('noteContent'); |
| | const searchInput = document.getElementById('searchInput'); |
| | |
| | |
| | document.addEventListener('DOMContentLoaded', () => { |
| | loadNotes(); |
| | renderNotes(); |
| | }); |
| | |
| | |
| | |
| | |
| | function loadNotes() { |
| | const storedNotes = localStorage.getItem('myNotesApp_data'); |
| | if (storedNotes) { |
| | notes = JSON.parse(storedNotes); |
| | } |
| | } |
| | |
| | |
| | function saveToLocalStorage() { |
| | localStorage.setItem('myNotesApp_data', JSON.stringify(notes)); |
| | renderNotes(); |
| | } |
| | |
| | |
| | function saveNote() { |
| | const title = titleInput.value.trim(); |
| | const content = contentInput.value.trim(); |
| | |
| | if (!title && !content) { |
| | alert('الرجاء كتابة عنوان أو محتوى للملاحظة'); |
| | return; |
| | } |
| | |
| | if (isEditing) { |
| | |
| | const noteIndex = notes.findIndex(n => n.id === currentEditId); |
| | if (noteIndex !== -1) { |
| | notes[noteIndex].title = title; |
| | notes[noteIndex].content = content; |
| | notes[noteIndex].lastModified = new Date().toISOString(); |
| | } |
| | } else { |
| | |
| | const newNote = { |
| | id: Date.now(), |
| | title: title, |
| | content: content, |
| | createdAt: new Date().toISOString(), |
| | lastModified: new Date().toISOString() |
| | }; |
| | notes.unshift(newNote); |
| | } |
| | |
| | saveToLocalStorage(); |
| | closeModal(); |
| | } |
| | |
| | |
| | function deleteNote(id) { |
| | if (confirm('هل أنت متأكد من حذف هذه الملاحظة؟')) { |
| | notes = notes.filter(note => note.id !== id); |
| | saveToLocalStorage(); |
| | } |
| | } |
| | |
| | |
| | function clearAllNotes() { |
| | if (notes.length === 0) return; |
| | |
| | if (confirm('تحذير: سيتم حذف جميع الملاحظات نهائياً. هل أنت متأكد؟')) { |
| | notes = []; |
| | saveToLocalStorage(); |
| | } |
| | } |
| | |
| | |
| | searchInput.addEventListener('input', (e) => { |
| | const searchTerm = e.target.value.toLowerCase(); |
| | const filteredNotes = notes.filter(note => |
| | note.title.toLowerCase().includes(searchTerm) || |
| | note.content.toLowerCase().includes(searchTerm) |
| | ); |
| | renderNotes(filteredNotes); |
| | }); |
| | |
| | |
| | |
| | |
| | function renderNotes(notesArray = notes) { |
| | notesGrid.innerHTML = ''; |
| | |
| | if (notesArray.length === 0) { |
| | emptyState.style.display = 'block'; |
| | return; |
| | } else { |
| | emptyState.style.display = 'none'; |
| | } |
| | |
| | |
| | |
| | const notesToRender = notesArray === notes ? [...notesArray].sort((a, b) => new Date(b.createdAt) - new Date(a.createdAt)) : notesArray; |
| | |
| | notesToRender.forEach(note => { |
| | const dateObj = new Date(note.createdAt); |
| | const dateStr = dateObj.toLocaleDateString('ar-EG', { |
| | year: 'numeric', |
| | month: 'long', |
| | day: 'numeric', |
| | hour: '2-digit', |
| | minute: '2-digit' |
| | }); |
| | |
| | const noteCard = document.createElement('div'); |
| | noteCard.className = 'note-card'; |
| | noteCard.innerHTML = ` |
| | <div class="note-header"> |
| | <h3 class="note-title">${escapeHtml(note.title) || '<i>بدون عنوان</i>'}</h3> |
| | <span class="note-date">${dateStr}</span> |
| | </div> |
| | <div class="note-content">${escapeHtml(note.content)}</div> |
| | <div class="note-actions"> |
| | <button class="icon-btn" onclick="editNote(${note.id})" title="تعديل"> |
| | <i class="fa-solid fa-pen-to-square"></i> |
| | </button> |
| | <button class="icon-btn delete" onclick="deleteNote(${note.id})" title="حذف"> |
| | <i class="fa-solid fa-trash"></i> |
| | </button> |
| | </div> |
| | `; |
| | notesGrid.appendChild(noteCard); |
| | }); |
| | } |
| | |
| | |
| | |
| | function openModal() { |
| | isEditing = false; |
| | currentEditId = null; |
| | modalTitleText.textContent = 'ملاحظة جديدة'; |
| | titleInput.value = ''; |
| | contentInput.value = ''; |
| | modal.classList.add('active'); |
| | titleInput.focus(); |
| | } |
| | |
| | function closeModal() { |
| | modal.classList.remove('active'); |
| | } |
| | |
| | function editNote(id) { |
| | const note = notes.find(n => n.id === id); |
| | if (note) { |
| | isEditing = true; |
| | currentEditId = id; |
| | modalTitleText.textContent = 'تعديل الملاحظة'; |
| | titleInput.value = note.title; |
| | contentInput.value = note.content; |
| | modal.classList.add('active'); |
| | } |
| | } |
| | |
| | |
| | modal.addEventListener('click', (e) => { |
| | if (e.target === modal) { |
| | closeModal(); |
| | } |
| | }); |
| | |
| | |
| | function escapeHtml(text) { |
| | if (!text) return ''; |
| | return text |
| | .replace(/&/g, "&") |
| | .replace(/</g, "<") |
| | .replace(/>/g, ">") |
| | .replace(/"/g, """) |
| | .replace(/'/g, "'"); |
| | } |
| | |
| | </script> |
| | </body> |
| | </html> |