Files
hamed-site/public/index.php
T

559 lines
28 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
spl_autoload_register(function ($class) {
$prefix = 'App\\';
$base_dir = __DIR__ . '/../src/';
$len = strlen($prefix);
if (strncmp($prefix, $class, $len) !== 0) {
return;
}
$relative_class = substr($class, $len);
$file = $base_dir . str_replace('\\', '/', $relative_class) . '.php';
if (file_exists($file)) {
require $file;
}
});
use App\Database;
use App\Album;
use App\Photo;
use App\Quote;
use App\Milestone;
// Handle JSON API Endpoints
if (isset($_GET['api'])) {
header('Content-Type: application/json; charset=utf-8');
$api = $_GET['api'];
try {
switch ($api) {
case 'photos':
$albumId = !empty($_GET['album_id']) ? (int)$_GET['album_id'] : null;
$favOnly = !empty($_GET['favorite_only']);
$search = !empty($_GET['search']) ? trim($_GET['search']) : null;
echo json_encode(['success' => true, 'data' => Photo::all($albumId, $favOnly, $search)]);
exit;
case 'albums':
echo json_encode(['success' => true, 'data' => Album::all()]);
exit;
case 'quotes':
echo json_encode(['success' => true, 'data' => Quote::all()]);
exit;
case 'milestones':
echo json_encode(['success' => true, 'data' => Milestone::all()]);
exit;
case 'stats':
echo json_encode(['success' => true, 'data' => Photo::getStats()]);
exit;
case 'like_photo':
$id = (int)($_GET['id'] ?? 0);
$likes = Photo::incrementLike($id);
echo json_encode(['success' => true, 'likes' => $likes]);
exit;
case 'toggle_fav':
$id = (int)($_GET['id'] ?? 0);
Photo::toggleFavorite($id);
echo json_encode(['success' => true]);
exit;
case 'delete_photo':
$id = (int)($_GET['id'] ?? 0);
Photo::delete($id);
echo json_encode(['success' => true]);
exit;
case 'delete_album':
$id = (int)($_GET['id'] ?? 0);
Album::delete($id);
echo json_encode(['success' => true]);
exit;
case 'delete_quote':
$id = (int)($_GET['id'] ?? 0);
Quote::delete($id);
echo json_encode(['success' => true]);
exit;
case 'delete_milestone':
$id = (int)($_GET['id'] ?? 0);
Milestone::delete($id);
echo json_encode(['success' => true]);
exit;
case 'upload_photo':
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
throw new Exception('متد نامعتبر است');
}
if (empty($_FILES['photo']) || $_FILES['photo']['error'] !== UPLOAD_ERR_OK) {
throw new Exception('لطفاً یک فایل تصویر معتبر انتخاب کنید');
}
$file = $_FILES['photo'];
$allowed = ['image/jpeg', 'image/png', 'image/webp', 'image/gif', 'image/svg+xml'];
$finfo = finfo_open(FILEINFO_MIME_TYPE);
$mime = finfo_file($finfo, $file['tmp_name']);
finfo_close($finfo);
if (!in_array($mime, $allowed)) {
throw new Exception('فرمت فایل پشتیبانی نمی‌شود (فقط JPG، PNG، WEBP، GIF)');
}
$ext = pathinfo($file['name'], PATHINFO_EXTENSION);
if (!$ext) $ext = 'jpg';
$filename = 'photo_' . time() . '_' . bin2hex(random_bytes(4)) . '.' . $ext;
$target = __DIR__ . '/uploads/' . $filename;
if (!move_uploaded_file($file['tmp_name'], $target)) {
throw new Exception('خطا در انتقال فایل ذخیره‌سازی');
}
$albumId = (int)($_POST['album_id'] ?? 1);
$title = trim($_POST['title'] ?? 'خاطره جدید');
$caption = trim($_POST['caption'] ?? '');
$ageTag = trim($_POST['age_tag'] ?? '۳ سالگی');
$photoDate = $_POST['photo_date'] ?? date('Y-m-d');
$photoId = Photo::create($albumId, $title, 'uploads/' . $filename, $caption, $ageTag, $photoDate);
echo json_encode(['success' => true, 'photo_id' => $photoId]);
exit;
case 'create_album':
$title = trim($_POST['title'] ?? '');
if (!$title) throw new Exception('عنوان آلبوم الزامی است');
$description = trim($_POST['description'] ?? '');
$icon = trim($_POST['icon'] ?? '🎈');
$color = trim($_POST['color'] ?? '#3b82f6');
$albumId = Album::create($title, $description, $icon, $color);
echo json_encode(['success' => true, 'album_id' => $albumId]);
exit;
case 'create_quote':
$quote = trim($_POST['quote'] ?? '');
if (!$quote) throw new Exception('متن جمله الزامی است');
$ageTag = trim($_POST['age_tag'] ?? '۳ سالگی');
$context = trim($_POST['context'] ?? '');
$saidDate = $_POST['said_date'] ?? date('Y-m-d');
$id = Quote::create($quote, $ageTag, $context, $saidDate);
echo json_encode(['success' => true, 'quote_id' => $id]);
exit;
case 'create_milestone':
$title = trim($_POST['title'] ?? '');
if (!$title) throw new Exception('عنوان دستاورد الزامی است');
$description = trim($_POST['description'] ?? '');
$ageTag = trim($_POST['age_tag'] ?? '۳ سالگی');
$icon = trim($_POST['icon'] ?? '🌟');
$milestoneDate = $_POST['milestone_date'] ?? date('Y-m-d');
$id = Milestone::create($title, $description, $ageTag, $icon, $milestoneDate);
echo json_encode(['success' => true, 'milestone_id' => $id]);
exit;
default:
echo json_encode(['success' => false, 'error' => 'مسیر یافت نشد']);
exit;
}
} catch (Throwable $e) {
echo json_encode(['success' => false, 'error' => $e->getMessage()]);
exit;
}
}
// Initial Data for HTML Render
$albums = Album::all();
$stats = Photo::getStats();
?>
<!DOCTYPE html>
<html lang="fa" dir="rtl">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>آلبوم خاطرات حامد کوچولو 🎈 پسر ۳ ساله</title>
<link rel="stylesheet" href="assets/css/style.css">
</head>
<body>
<!-- Floating Background Shapes -->
<div class="floating-shapes">
<div class="shape">🎈</div>
<div class="shape">⭐</div>
<div class="shape">🧸</div>
<div class="shape">🚗</div>
<div class="shape">🎨</div>
</div>
<div class="app-container">
<!-- Hero Header -->
<header class="hero-card">
<div class="child-profile">
<div class="avatar-badge">👶</div>
<div class="child-info">
<h1>
<span>آلبوم خاطرات حامد کوچولو</span>
<span>🎈</span>
</h1>
<p>گنجینه عکس‌ها، خنده‌های شیرین، اولین حرف‌ها و لحظات ناب رشد پسر ۳ ساله ما 💖</p>
<div class="badge-age">
<span>🎂</span>
<span>۳ سال و ۲ ماهه (متولد تابستان ۱۴۰۲)</span>
</div>
</div>
</div>
<div class="quick-stats">
<div class="stat-box">
<div class="stat-number" id="statPhotosCount"><?= $stats['photos'] ?></div>
<div class="stat-label">📸 عکس شیرین</div>
</div>
<div class="stat-box">
<div class="stat-number" id="statAlbumsCount"><?= $stats['albums'] ?></div>
<div class="stat-label">📂 آلبوم موضوعی</div>
</div>
<div class="stat-box">
<div class="stat-number" id="statQuotesCount"><?= $stats['quotes'] ?></div>
<div class="stat-label">💬 جمله بامزه</div>
</div>
<div class="stat-box">
<div class="stat-number" id="statMilestonesCount"><?= $stats['milestones'] ?></div>
<div class="stat-label">🌟 دستاورد رشد</div>
</div>
</div>
</header>
<!-- Navigation & Main Actions -->
<nav class="action-bar">
<div class="nav-tabs">
<button class="tab-btn active" data-tab="gallery" onclick="switchTab('gallery')">
<span>📸</span>
<span>گالری عکس‌ها</span>
</button>
<button class="tab-btn" data-tab="albums" onclick="switchTab('albums')">
<span>📂</span>
<span>آلبوم‌ها</span>
</button>
<button class="tab-btn" data-tab="quotes" onclick="switchTab('quotes')">
<span>💬</span>
<span>حرف‌های بامزه</span>
</button>
<button class="tab-btn" data-tab="milestones" onclick="switchTab('milestones')">
<span>🌟</span>
<span>خط رشد</span>
</button>
<button class="tab-btn" data-tab="slideshow" onclick="switchTab('slideshow')">
<span>🎵</span>
<span>اسلایدشو موزیکال</span>
</button>
</div>
<div class="btn-group-actions">
<button class="btn btn-primary" onclick="openModal('uploadPhotoModal')">
<span></span>
<span>افزودن عکس جدید</span>
</button>
<button class="btn btn-accent" onclick="openModal('newAlbumModal')">
<span>📁</span>
<span>ایجاد آلبوم</span>
</button>
</div>
</nav>
<!-- TAB 1: Gallery -->
<section id="tab-gallery" class="tab-pane">
<div class="filters-bar">
<div class="album-filter-scroll">
<button class="filter-chip active" onclick="filterByAlbum(null, this)">همه عکس‌ها</button>
<?php foreach ($albums as $alb): ?>
<button class="filter-chip" onclick="filterByAlbum(<?= $alb['id'] ?>, this)">
<?= htmlspecialchars($alb['icon'] . ' ' . $alb['title']) ?>
</button>
<?php endforeach; ?>
</div>
<div style="display: flex; gap: 10px; align-items: center; flex-wrap: wrap;">
<button id="favFilterBtn" class="btn btn-outline" onclick="toggleFavoriteFilter()" style="padding: 6px 14px; font-size: 0.85rem;">
⭐ فقط برگزیده‌ها
</button>
<div class="search-box">
<span class="search-icon">🔍</span>
<input type="text" id="searchInput" placeholder="جستجو در خاطرات و عکس‌ها...">
</div>
</div>
</div>
<!-- Photos Grid -->
<div class="gallery-grid" id="photosGrid">
<!-- Dynamically loaded via JS -->
</div>
</section>
<!-- TAB 2: Albums -->
<section id="tab-albums" class="tab-pane" style="display: none;">
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px;">
<h2 style="font-size: 1.3rem; font-weight: 800; color: #1e293b;">📂 دسته‌بندی آلبوم‌های خاطرات</h2>
<button class="btn btn-primary" onclick="openModal('newAlbumModal')">+ آلبوم جدید</button>
</div>
<div class="albums-grid" id="albumsGrid">
<!-- Dynamically loaded via JS -->
</div>
</section>
<!-- TAB 3: Quotes -->
<section id="tab-quotes" class="tab-pane" style="display: none;">
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px;">
<div>
<h2 style="font-size: 1.3rem; font-weight: 800; color: #1e293b;">💬 دفترچه جملات و حرف‌های بامزه</h2>
<p style="font-size: 0.88rem; color: #64748b; margin-top: 4px;">ثبت تکه‌کلام‌ها و حرف‌های شیرین و خنده‌دار ۳ سالگی</p>
</div>
<button class="btn btn-primary" onclick="openModal('newQuoteModal')">+ ثبت حرف بامزه</button>
</div>
<div class="quotes-grid" id="quotesGrid">
<!-- Dynamically loaded via JS -->
</div>
</section>
<!-- TAB 4: Milestones -->
<section id="tab-milestones" class="tab-pane" style="display: none;">
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px;">
<div>
<h2 style="font-size: 1.3rem; font-weight: 800; color: #1e293b;">🌟 خط زمانی رشد و دستاوردهای ۳ سالگی</h2>
<p style="font-size: 0.88rem; color: #64748b; margin-top: 4px;">مراحل مهم، مهارت‌های جدید و افتخارات کوچولوی دوست‌داشتنی</p>
</div>
<button class="btn btn-primary" onclick="openModal('newMilestoneModal')">+ ثبت دستاورد جدید</button>
</div>
<div class="timeline-container" id="milestonesList">
<!-- Dynamically loaded via JS -->
</div>
</section>
<!-- TAB 5: Slideshow -->
<section id="tab-slideshow" class="tab-pane" style="display: none;">
<div style="text-align: center; margin-bottom: 20px;">
<h2 style="font-size: 1.4rem; font-weight: 800; color: #1e293b; margin-bottom: 8px;">🎵 اسلایدشو موزیکال خاطرات</h2>
<button id="musicToggleBtn" class="btn btn-accent" onclick="toggleLullaby()">
🎵 پخش موزیک آرامش‌بخش
</button>
</div>
<div style="background: #0f172a; border-radius: var(--radius-xl); overflow: hidden; padding: 24px; text-align: center; box-shadow: var(--card-shadow); max-width: 900px; margin: 0 auto;">
<div style="height: 480px; display: flex; align-items: center; justify-content: center; margin-bottom: 16px;">
<img id="slideshowCurrentImg" src="" style="max-height: 100%; max-width: 100%; object-fit: contain; border-radius: 12px; transition: opacity 0.5s ease;" alt="Slideshow">
</div>
<h3 id="slideshowCurrentTitle" style="color: #ffffff; font-size: 1.3rem; font-weight: 800; margin-bottom: 6px;"></h3>
<p id="slideshowCurrentDesc" style="color: #94a3b8; font-size: 0.95rem;"></p>
</div>
</section>
</div>
<!-- MODAL: Upload Photo -->
<div class="modal-overlay" id="uploadPhotoModal">
<div class="modal-card">
<button class="modal-close-btn" onclick="closeModal('uploadPhotoModal')">✕</button>
<div class="modal-header">
<h2>📸 افزودن عکس خاطره‌انگیز جدید</h2>
</div>
<form id="uploadPhotoForm" onsubmit="submitPhotoUpload(event)">
<div class="form-group">
<label>فایل تصویر</label>
<div class="file-drop-area" id="fileDropArea">
<div id="dropText">
<span style="font-size: 2.5rem;">📷</span>
<p style="font-weight: 700; color: #1e40af; margin-top: 8px;">برای انتخاب یا رها کردن عکس کلیک کنید</p>
<p style="font-size: 0.8rem; color: #64748b;">(پشتیبانی از JPG, PNG, WEBP, GIF)</p>
</div>
<div id="photoPreviewContainer" style="display: none;">
<img id="photoPreviewImg" src="" style="max-height: 180px; border-radius: 8px;" alt="پیش‌نمایش">
<p style="font-size: 0.8rem; color: #059669; font-weight: 700; margin-top: 6px;">تصویر با موفقیت انتخاب شد ✓</p>
</div>
<input type="file" id="photoFileInput" name="photo" accept="image/*" style="display: none;" required>
</div>
</div>
<div class="form-group">
<label>عنوان عکس یا مناسبت</label>
<input type="text" name="title" class="form-control" placeholder="مثلاً: بازی با شن‌های ساحل" required>
</div>
<div class="form-group">
<label>آلبوم مربوطه</label>
<select name="album_id" class="form-control" id="uploadAlbumSelect">
<?php foreach ($albums as $alb): ?>
<option value="<?= $alb['id'] ?>"><?= htmlspecialchars($alb['icon'] . ' ' . $alb['title']) ?></option>
<?php endforeach; ?>
</select>
</div>
<div class="form-group">
<label>توضیحات و خاطره این عکس</label>
<textarea name="caption" class="form-control" rows="3" placeholder="خاطره، حرف جالبی که زد یا حس و حال اون لحظه..."></textarea>
</div>
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 12px;">
<div class="form-group">
<label>برچسب سن کودک</label>
<input type="text" name="age_tag" class="form-control" value="۳ سالگی" placeholder="مثلاً: ۳ سال و ۲ ماهگی">
</div>
<div class="form-group">
<label>تاریخ ثبت عکس</label>
<input type="date" name="photo_date" class="form-control" value="<?= date('Y-m-d') ?>">
</div>
</div>
<button type="submit" class="btn btn-primary" style="width: 100%; justify-content: center; margin-top: 10px; padding: 12px;">
ذخیره در آلبوم 🎈
</button>
</form>
</div>
</div>
<!-- MODAL: New Album -->
<div class="modal-overlay" id="newAlbumModal">
<div class="modal-card">
<button class="modal-close-btn" onclick="closeModal('newAlbumModal')">✕</button>
<div class="modal-header">
<h2>📁 ایجاد آلبوم جدید</h2>
</div>
<form id="newAlbumForm" onsubmit="submitNewAlbum(event)">
<div class="form-group">
<label>نام آلبوم</label>
<input type="text" name="title" class="form-control" placeholder="مثلاً: بازی‌های مهدکودک" required>
</div>
<div class="form-group">
<label>توضیحات مختصر</label>
<textarea name="description" class="form-control" rows="2" placeholder="توضیح کوتاه درباره عکس‌های این آلبوم..."></textarea>
</div>
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 12px;">
<div class="form-group">
<label>آیکون ایموجی</label>
<input type="text" name="icon" class="form-control" value="🎈" placeholder="🎂, 🎠, 🎨, 🚗...">
</div>
<div class="form-group">
<label>رنگ تم</label>
<input type="color" name="color" class="form-control" value="#3b82f6" style="height: 44px; padding: 4px;">
</div>
</div>
<button type="submit" class="btn btn-primary" style="width: 100%; justify-content: center; margin-top: 10px;">
ساخت آلبوم ✨
</button>
</form>
</div>
</div>
<!-- MODAL: New Quote -->
<div class="modal-overlay" id="newQuoteModal">
<div class="modal-card">
<button class="modal-close-btn" onclick="closeModal('newQuoteModal')">✕</button>
<div class="modal-header">
<h2>💬 ثبت جمله و حرف بامزه</h2>
</div>
<form id="newQuoteForm" onsubmit="submitNewQuote(event)">
<div class="form-group">
<label>جمله یا حرفی که پسر گلمون زد</label>
<textarea name="quote" class="form-control" rows="3" placeholder="دقیقاً چی گفت؟..." required></textarea>
</div>
<div class="form-group">
<label>موقعیت یا شرایطی که گفت</label>
<input type="text" name="context" class="form-control" placeholder="مثلاً: موقع خوابیدن، وسط غذا خوردن...">
</div>
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 12px;">
<div class="form-group">
<label>سن کودک در زمان گفتن</label>
<input type="text" name="age_tag" class="form-control" value="۳ سالگی">
</div>
<div class="form-group">
<label>تاریخ</label>
<input type="date" name="said_date" class="form-control" value="<?= date('Y-m-d') ?>">
</div>
</div>
<button type="submit" class="btn btn-primary" style="width: 100%; justify-content: center; margin-top: 10px;">
ذخیره در دفترچه خاطرات 🧸
</button>
</form>
</div>
</div>
<!-- MODAL: New Milestone -->
<div class="modal-overlay" id="newMilestoneModal">
<div class="modal-card">
<button class="modal-close-btn" onclick="closeModal('newMilestoneModal')">✕</button>
<div class="modal-header">
<h2>🌟 ثبت دستاورد رشد جدید</h2>
</div>
<form id="newMilestoneForm" onsubmit="submitNewMilestone(event)">
<div class="form-group">
<label>عنوان دستاورد یا مهارت جدید</label>
<input type="text" name="title" class="form-control" placeholder="مثلاً: یادگیری پوشیدن کفش به تنهایی" required>
</div>
<div class="form-group">
<label>توضیحات و جزئیات</label>
<textarea name="description" class="form-control" rows="2" placeholder="چطور انجام داد و چه عکس‌العملی داشت..."></textarea>
</div>
<div style="display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 10px;">
<div class="form-group">
<label>ایموجی</label>
<input type="text" name="icon" class="form-control" value="🌟">
</div>
<div class="form-group">
<label>سن</label>
<input type="text" name="age_tag" class="form-control" value="۳ سالگی">
</div>
<div class="form-group">
<label>تاریخ</label>
<input type="date" name="milestone_date" class="form-control" value="<?= date('Y-m-d') ?>">
</div>
</div>
<button type="submit" class="btn btn-primary" style="width: 100%; justify-content: center; margin-top: 10px;">
ثبت در خط زمانی 🚀
</button>
</form>
</div>
</div>
<!-- MODAL: Lightbox Viewer -->
<div class="modal-overlay" id="lightboxModal">
<div class="modal-card lightbox-modal-card">
<button class="modal-close-btn" style="z-index: 10; background: rgba(255,255,255,0.8);" onclick="closeModal('lightboxModal')">✕</button>
<div class="lightbox-content">
<div class="lightbox-img-area">
<button onclick="prevLightbox()" style="position: absolute; right: 12px; background: rgba(0,0,0,0.5); color: #fff; border: none; font-size: 1.5rem; width: 44px; height: 44px; border-radius: 50%; cursor: pointer; z-index: 5;"></button>
<img id="lightboxImg" src="" alt="بزرگنمایی">
<button onclick="nextLightbox()" style="position: absolute; left: 12px; background: rgba(0,0,0,0.5); color: #fff; border: none; font-size: 1.5rem; width: 44px; height: 44px; border-radius: 50%; cursor: pointer; z-index: 5;"></button>
</div>
<div class="lightbox-sidebar">
<div>
<span id="lightboxAlbum" class="photo-album-tag" style="font-size: 0.9rem; margin-bottom: 8px;"></span>
<h2 id="lightboxTitle" style="font-size: 1.3rem; font-weight: 800; margin-bottom: 10px;"></h2>
<p id="lightboxCaption" style="font-size: 0.92rem; color: #475569; line-height: 1.6;"></p>
</div>
<div style="border-top: 1px solid #e2e8f0; padding-top: 16px; margin-top: 20px;">
<div style="display: flex; justify-content: space-between; font-size: 0.85rem; color: #64748b; margin-bottom: 14px;">
<span>🏷️ سن: <strong id="lightboxAge"></strong></span>
<span>📅 تاریخ: <strong id="lightboxDate"></strong></span>
<span>❤️ لایک: <strong id="lightboxLikes"></strong></span>
</div>
<div style="display: flex; gap: 10px;">
<a id="lightboxDownloadBtn" href="" download class="btn btn-outline" style="flex: 1; text-align: center; text-decoration: none; justify-content: center;">
⬇️ دانلود عکس
</a>
</div>
</div>
</div>
</div>
</div>
</div>
<script src="assets/js/app.js"></script>
</body>
</html>