anycoder-5dc7bf33 / index.html
moha77edhassan's picture
Upload folder using huggingface_hub
2d8ab1b verified
<!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>
<!-- استيراد خط Cairo و مكتبة أيقونات FontAwesome -->
<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>
/*
* إعدادات المتغيرات والألوان (Color Palette)
* تصميم Minimal بألوان هادئة (بنفسجي فاتح وأبيض)
*/
: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;
}
/* --- حاوية الملاحظات (Grid) --- */
.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; /* عرض 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) --- */
.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;
}
/* --- التجاوب (Responsive) --- */
@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">
<!-- سيتم توليد الملاحظات هنا بواسطة JavaScript -->
</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>
<!-- النافذة المنبثقة (Modal) -->
<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()">&times;</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>
// --- متغيرات الحالة (State) ---
let notes = [];
let isEditing = false;
let currentEditId = null;
// --- عناصر DOM ---
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();
});
// --- الوظائف الأساسية (CRUD) ---
// 1. تحميل الملاحظات من LocalStorage
function loadNotes() {
const storedNotes = localStorage.getItem('myNotesApp_data');
if (storedNotes) {
notes = JSON.parse(storedNotes);
}
}
// 2. حفظ الملاحظات في LocalStorage
function saveToLocalStorage() {
localStorage.setItem('myNotesApp_data', JSON.stringify(notes));
renderNotes();
}
// 3. إضافة أو تعديل ملاحظة
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();
}
// 4. حذف ملاحظة
function deleteNote(id) {
if (confirm('هل أنت متأكد من حذف هذه الملاحظة؟')) {
notes = notes.filter(note => note.id !== id);
saveToLocalStorage();
}
}
// 5. حذف جميع الملاحظات
function clearAllNotes() {
if (notes.length === 0) return;
if (confirm('تحذير: سيتم حذف جميع الملاحظات نهائياً. هل أنت متأكد؟')) {
notes = [];
saveToLocalStorage();
}
}
// 6. البحث في الملاحظات
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);
});
// --- وظائف العرض (UI) ---
// عرض الملاحظات
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);
});
}
// --- وظائف المودال (Modal) ---
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();
}
});
// دالة مساعدة لمنع حقن HTML (Security)
function escapeHtml(text) {
if (!text) return '';
return text
.replace(/&/g, "&amp;")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
.replace(/"/g, "&quot;")
.replace(/'/g, "&#039;");
}
</script>
</body>
</html>