Compare commits

...

1 Commits

Author SHA1 Message Date
Flatlogic Bot
76b95bec18 v1 2025-11-25 15:04:51 +00:00
25 changed files with 2063 additions and 148 deletions

9
admin/auth.php Normal file
View File

@ -0,0 +1,9 @@
<?php
require_once __DIR__ . '/../auth.php';
// Role-based access control
if (!isset($_SESSION['role']) || !in_array($_SESSION['role'], ['admin', 'super_admin'])) {
// If the user is not an admin, show an error and stop execution.
http_response_code(403);
die("Forbidden: You do not have permission to access this page.");
}

170
admin/monitoring.php Normal file
View File

@ -0,0 +1,170 @@
<?php
// admin/monitoring.php
require_once '../auth.php'; // Ensure user is authenticated and is an admin
require_once '../db/config.php'; // Database connection
// Restrict access to admins
if (!isset($_SESSION['role']) || !in_array($_SESSION['role'], ['admin', 'super_admin'])) {
header("Location: /index.php");
exit();
}
$pageTitle = "Music Monitoring";
$message = '';
if (isset($_POST['upload_csv'])) {
if (isset($_FILES['csv_file']) && $_FILES['csv_file']['error'] == 0) {
$fileName = $_FILES['csv_file']['tmp_name'];
$file = fopen($fileName, "r");
// Skip the header row
fgetcsv($file);
$stmt = db()->prepare("INSERT INTO monitoring_data (track_id, plays, date) VALUES (?, ?, ?)");
while (($column = fgetcsv($file, 10000, ",")) !== FALSE) {
$track_id = $column[0];
$plays = $column[1];
$date = $column[2];
$stmt->execute([$track_id, $plays, $date]);
}
fclose($file);
$message = "<div class='alert alert-success'>CSV file has been successfully uploaded and processed.</div>";
} else {
$message = "<div class='alert alert-danger'>Error uploading file.</div>";
}
}
// Fetch monitoring data
$stmt = db()->query("
SELECT t.title, md.plays, md.date
FROM monitoring_data md
JOIN tracks t ON md.track_id = t.id
ORDER BY md.date DESC
");
$monitoring_data = $stmt->fetchAll(PDO::FETCH_ASSOC);
// Prepare data for the chart
$chart_labels = [];
$chart_data = [];
foreach ($monitoring_data as $data) {
$chart_labels[] = $data['title'];
$chart_data[] = $data['plays'];
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title><?php echo $pageTitle; ?> - Zeal Music</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/css/all.min.css">
<link rel="stylesheet" href="/assets/css/custom.css?v=<?php echo time(); ?>">
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
</head>
<body>
<div class="dashboard-container">
<?php include '../partials/sidebar.php'; ?>
<main class="main-content">
<header class="page-header">
<h1><?php echo $pageTitle; ?></h1>
<div class="user-info">
<p>Welcome, <?php echo htmlspecialchars($_SESSION['username']); ?></p>
</div>
</header>
<section class="content-section">
<div class="card">
<div class="card-header">
<h2>Upload CSV for Music Monitoring</h2>
</div>
<div class="card-body">
<?php echo $message; ?>
<p>Select a CSV file containing music monitoring data to upload. The file should contain columns for Track ID, Play Count, and Date.</p>
<form action="monitoring.php" method="post" enctype="multipart/form-data" class="upload-form">
<div class="form-group">
<label for="csv_file">CSV File</label>
<input type="file" name="csv_file" id="csv_file" accept=".csv" required>
</div>
<button type="submit" name="upload_csv" class="btn btn-primary">Upload CSV</button>
</form>
</div>
</div>
</section>
<section class="content-section">
<div class="card">
<div class="card-header">
<h2>Monitoring Data Overview</h2>
</div>
<div class="card-body">
<canvas id="monitoringChart"></canvas>
</div>
</div>
</section>
<section class="content-section">
<div class="card">
<div class="card-header">
<h2>Raw Monitoring Data</h2>
</div>
<div class="card-body">
<table class="data-table">
<thead>
<tr>
<th>Track Title</th>
<th>Plays</th>
<th>Date</th>
</tr>
</thead>
<tbody>
<?php if (empty($monitoring_data)): ?>
<tr>
<td colspan="3">No monitoring data found.</td>
</tr>
<?php else: ?>
<?php foreach ($monitoring_data as $data): ?>
<tr>
<td><?php echo htmlspecialchars($data['title']); ?></td>
<td><?php echo htmlspecialchars($data['plays']); ?></td>
<td><?php echo htmlspecialchars($data['date']); ?></td>
</tr>
<?php endforeach; ?>
<?php endif; ?>
</tbody>
</table>
</div>
</div>
</section>
</main>
</div>
<script src="/assets/js/main.js?v=<?php echo time(); ?>"></script>
<script>
const ctx = document.getElementById('monitoringChart').getContext('2d');
new Chart(ctx, {
type: 'bar',
data: {
labels: <?php echo json_encode($chart_labels); ?>,
datasets: [{
label: '# of Plays',
data: <?php echo json_encode($chart_data); ?>,
backgroundColor: 'rgba(75, 192, 192, 0.2)',
borderColor: 'rgba(75, 192, 192, 1)',
borderWidth: 1
}]
},
options: {
scales: {
y: {
beginAtZero: true
}
}
}
});
</script>
</body>
</html>

122
admin/packages.php Normal file
View File

@ -0,0 +1,122 @@
<?php
require_once '../auth.php';
require_once '../db/config.php';
// Only admins can access this page
if (!isset($_SESSION['role']) || !in_array($_SESSION['role'], ['admin', 'super_admin'])) {
header('Location: /login.php');
exit;
}
$pageTitle = "Manage Packages";
// Handle form submissions
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
if (isset($_POST['create_package'])) {
$name = $_POST['name'];
$price = $_POST['price'];
$features = $_POST['features'];
$is_recommended = isset($_POST['is_recommended']) ? 1 : 0;
$stmt = db()->prepare("INSERT INTO packages (name, price, features, is_recommended) VALUES (?, ?, ?, ?)");
$stmt->execute([$name, $price, $features, $is_recommended]);
} elseif (isset($_POST['update_package'])) {
$id = $_POST['id'];
$name = $_POST['name'];
$price = $_POST['price'];
$features = $_POST['features'];
$is_recommended = isset($_POST['is_recommended']) ? 1 : 0;
$stmt = db()->prepare("UPDATE packages SET name = ?, price = ?, features = ?, is_recommended = ? WHERE id = ?");
$stmt->execute([$name, $price, $features, $is_recommended, $id]);
} elseif (isset($_POST['delete_package'])) {
$id = $_POST['id'];
$stmt = db()->prepare("DELETE FROM packages WHERE id = ?");
$stmt->execute([$id]);
}
header('Location: packages.php');
exit;
}
// Fetch all packages
$stmt = db()->query("SELECT * FROM packages ORDER BY price");
$packages = $stmt->fetchAll(PDO::FETCH_ASSOC);
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title><?php echo htmlspecialchars($pageTitle); ?></title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<link rel="stylesheet" href="../assets/css/custom.css?v=<?php echo time(); ?>">
</head>
<body class="dark-theme">
<div class="page-container">
<?php include '../partials/sidebar.php'; ?>
<main class="main-content">
<header class="main-header">
<h1><?php echo htmlspecialchars($pageTitle); ?></h1>
</header>
<div class="content-wrapper">
<div class="content-card">
<h2>Add New Package</h2>
<form action="packages.php" method="POST" class="upload-form">
<div class="form-group">
<label for="name">Package Name</label>
<input type="text" id="name" name="name" required>
</div>
<div class="form-group">
<label for="price">Price</label>
<input type="number" step="0.01" id="price" name="price" required>
</div>
<div class="form-group">
<label for="features">Features (comma-separated)</label>
<input type="text" id="features" name="features" required>
</div>
<div class="form-group-checkbox">
<input type="checkbox" id="is_recommended" name="is_recommended">
<label for="is_recommended">Recommend this package</label>
</div>
<button type="submit" name="create_package" class="btn btn-primary">Create Package</button>
</form>
</div>
<div class="content-card mt-4">
<h2>Existing Packages</h2>
<div class="table-responsive">
<table class="table">
<thead>
<tr>
<th>Name</th>
<th>Price</th>
<th>Features</th>
<th>Recommended</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<?php foreach ($packages as $package): ?>
<tr>
<td><?php echo htmlspecialchars($package['name']); ?></td>
<td>$<?php echo htmlspecialchars($package['price']); ?></td>
<td><?php echo htmlspecialchars($package['features']); ?></td>
<td><?php echo $package['is_recommended'] ? 'Yes' : 'No'; ?></td>
<td>
<form action="packages.php" method="POST" style="display:inline;">
<input type="hidden" name="id" value="<?php echo $package['id']; ?>">
<button type="submit" name="delete_package" class="btn btn-danger">Delete</button>
</form>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
</div>
</div>
</main>
</div>
</body>
</html>

151
admin/stations.php Normal file
View File

@ -0,0 +1,151 @@
<?php
require_once __DIR__ . '/../db/config.php';
require_once __DIR__ . '/../auth.php'; // General login check
// Role-based access control
if (!isset($_SESSION['role']) || !in_array($_SESSION['role'], ['admin', 'super_admin'])) {
http_response_code(403);
die("Forbidden: You do not have permission to access this page.");
}
$pageTitle = "Manage Stations - Zeal Music";
$pdo = db();
$stations = [];
$errors = [];
$successMessage = '';
// Handle POST requests (add/update/delete)
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$action = $_POST['action'] ?? '';
try {
if ($action === 'add') {
$name = trim($_POST['name']);
$email = trim($_POST['contact_email']);
if (!empty($name) && filter_var($email, FILTER_VALIDATE_EMAIL)) {
$stmt = $pdo->prepare("INSERT INTO stations (name, contact_email, genre_focus, website) VALUES (?, ?, ?, ?)");
$stmt->execute([$name, $email, $_POST['genre_focus'], $_POST['website']]);
$successMessage = "Station added successfully.";
} else {
$errors[] = "Invalid name or email for new station.";
}
} elseif ($action === 'delete') {
$id = filter_input(INPUT_POST, 'id', FILTER_VALIDATE_INT);
if ($id) {
$stmt = $pdo->prepare("DELETE FROM stations WHERE id = ?");
$stmt->execute([$id]);
$successMessage = "Station deleted successfully.";
}
}
} catch (PDOException $e) {
$errors[] = "Database error: " . $e->getMessage();
}
}
// Fetch all stations for display
try {
$stmt = $pdo->query("SELECT * FROM stations ORDER BY name");
$stations = $stmt->fetchAll(PDO::FETCH_ASSOC);
} catch (PDOException $e) {
$errors[] = "Error fetching stations: " . $e->getMessage();
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title><?php echo htmlspecialchars($pageTitle); ?></title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<link rel="stylesheet" href="../assets/css/custom.css?v=<?php echo time(); ?>">
</head>
<body class="dark-theme">
<div class="page-container">
<!-- Sidebar -->
<?php include __DIR__ . '/../partials/sidebar.php'; ?>
<!-- Main Content -->
<main class="main-content">
<header class="main-header">
<h1>Manage Radio Stations</h1>
<div class="user-profile">
<i class="fa fa-user-shield"></i>
<span><?php echo htmlspecialchars($_SESSION['username']); ?> (Admin)</span>
</div>
</header>
<div class="content-wrapper">
<?php if ($successMessage) echo "<div class='alert alert-success'>".htmlspecialchars($successMessage)."</div>"; ?>
<?php foreach ($errors as $error) echo "<div class='alert alert-danger'>".htmlspecialchars($error)."</div>"; ?>
<!-- Add Station Form -->
<div class="content-card mb-4">
<h2>Add New Station</h2>
<form action="stations.php" method="POST" class="upload-form" style="max-width: none;">
<input type="hidden" name="action" value="add">
<div style="display: flex; gap: 1rem; align-items: flex-end;">
<div class="form-group" style="flex: 2;">
<label for="name">Station Name</label>
<input type="text" name="name" required>
</div>
<div class="form-group" style="flex: 2;">
<label for="contact_email">Contact Email</label>
<input type="email" name="contact_email" required>
</div>
<div class="form-group" style="flex: 1;">
<label for="genre_focus">Genres</label>
<input type="text" name="genre_focus">
</div>
<div class="form-group" style="flex: 1;">
<label for="website">Website</label>
<input type="text" name="website">
</div>
<div class="form-group">
<button type="submit" class="btn btn-primary">Add Station</button>
</div>
</div>
</form>
</div>
<!-- Stations List -->
<div class="content-card">
<h2>Existing Stations</h2>
<table style="width: 100%;">
<thead>
<tr style="text-align: left; border-bottom: 1px solid #404040;">
<th style="padding: 10px;">Name</th>
<th style="padding: 10px;">Email</th>
<th style="padding: 10px;">Genres</th>
<th style="padding: 10px;">Website</th>
<th style="padding: 10px;">Actions</th>
</tr>
</thead>
<tbody>
<?php if (empty($stations)): ?>
<tr><td colspan="5" style="padding: 20px; text-align: center; color: #b3b3b3;">No stations found.</td></tr>
<?php else: ?>
<?php foreach ($stations as $station): ?>
<tr style="border-bottom: 1px solid #282828;">
<td style="padding: 10px;"><?= htmlspecialchars($station['name']) ?></td>
<td style="padding: 10px;"><?= htmlspecialchars($station['contact_email']) ?></td>
<td style="padding: 10px;"><?= htmlspecialchars($station['genre_focus']) ?></td>
<td style="padding: 10px;"><a href="<?= htmlspecialchars($station['website']) ?>" target="_blank"><?= htmlspecialchars($station['website']) ?></a></td>
<td style="padding: 10px;">
<form action="stations.php" method="POST" onsubmit="return confirm('Are you sure you want to delete this station?');" style="display: inline;">
<input type="hidden" name="action" value="delete">
<input type="hidden" name="id" value="<?= $station['id'] ?>">
<button type="submit" class="btn" style="background: none; color: #e76f51;"><i class="fa fa-trash"></i></button>
</form>
</td>
</tr>
<?php endforeach; ?>
<?php endif; ?>
</tbody>
</table>
</div>
</div>
</main>
</div>
</body>
</html>

157
admin/users.php Normal file
View File

@ -0,0 +1,157 @@
<?php
require_once __DIR__ . '/../db/config.php';
require_once __DIR__ . '/../auth.php'; // General login check
// Role-based access control
if (!isset($_SESSION['role']) || !in_array($_SESSION['role'], ['admin', 'super_admin'])) {
http_response_code(403);
die("Forbidden: You do not have permission to access this page.");
}
$pageTitle = "Manage Users - Zeal Music";
$pdo = db();
$users = [];
$errors = [];
$successMessage = '';
// Handle POST requests (add/update/delete)
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$action = $_POST['action'] ?? '';
try {
if ($action === 'add') {
$username = trim($_POST['username']);
$email = trim($_POST['email']);
$password = $_POST['password'];
$role = $_POST['role'];
if (!empty($username) && filter_var($email, FILTER_VALIDATE_EMAIL) && !empty($password)) {
$hashedPassword = password_hash($password, PASSWORD_DEFAULT);
$stmt = $pdo->prepare("INSERT INTO users (username, email, password, role) VALUES (?, ?, ?, ?)");
$stmt->execute([$username, $email, $hashedPassword, $role]);
$successMessage = "User added successfully.";
} else {
$errors[] = "Invalid username, email, or password for new user.";
}
} elseif ($action === 'delete') {
$id = filter_input(INPUT_POST, 'id', FILTER_VALIDATE_INT);
if ($id) {
$stmt = $pdo->prepare("DELETE FROM users WHERE id = ?");
$stmt->execute([$id]);
$successMessage = "User deleted successfully.";
}
}
} catch (PDOException $e) {
$errors[] = "Database error: " . $e->getMessage();
}
}
// Fetch all users for display
try {
$stmt = $pdo->query("SELECT id, username, email, role FROM users ORDER BY username");
$users = $stmt->fetchAll(PDO::FETCH_ASSOC);
} catch (PDOException $e) {
$errors[] = "Error fetching users: " . $e->getMessage();
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title><?php echo htmlspecialchars($pageTitle); ?></title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<link rel="stylesheet" href="../assets/css/custom.css?v=<?php echo time(); ?>">
</head>
<body class="dark-theme">
<div class="page-container">
<!-- Sidebar -->
<?php include __DIR__ . '/../partials/sidebar.php'; ?>
<!-- Main Content -->
<main class="main-content">
<header class="main-header">
<h1>Manage Users</h1>
<div class="user-profile">
<i class="fa fa-user-shield"></i>
<span><?php echo htmlspecialchars($_SESSION['username']); ?> (Admin)</span>
</div>
</header>
<div class="content-wrapper">
<?php if ($successMessage) echo "<div class='alert alert-success'>".htmlspecialchars($successMessage)."</div>"; ?>
<?php foreach ($errors as $error) echo "<div class='alert alert-danger'>".htmlspecialchars($error)."</div>"; ?>
<!-- Add User Form -->
<div class="content-card mb-4">
<h2>Add New User</h2>
<form action="users.php" method="POST" class="upload-form" style="max-width: none;">
<input type="hidden" name="action" value="add">
<div style="display: flex; gap: 1rem; align-items: flex-end;">
<div class="form-group" style="flex: 1;">
<label for="username">Username</label>
<input type="text" name="username" required>
</div>
<div class="form-group" style="flex: 2;">
<label for="email">Email</label>
<input type="email" name="email" required>
</div>
<div class="form-group" style="flex: 1;">
<label for="password">Password</label>
<input type="password" name="password" required>
</div>
<div class="form-group" style="flex: 1;">
<label for="role">Role</label>
<select name="role">
<option value="artist">Artist</option>
<option value="admin">Admin</option>
<option value="super_admin">Super Admin</option>
</select>
</div>
<div class="form-group">
<button type="submit" class="btn btn-primary">Add User</button>
</div>
</div>
</form>
</div>
<!-- Users List -->
<div class="content-card">
<h2>Existing Users</h2>
<table style="width: 100%;">
<thead>
<tr style="text-align: left; border-bottom: 1px solid #404040;">
<th style="padding: 10px;">Username</th>
<th style="padding: 10px;">Email</th>
<th style="padding: 10px;">Role</th>
<th style="padding: 10px;">Actions</th>
</tr>
</thead>
<tbody>
<?php if (empty($users)): ?>
<tr><td colspan="4" style="padding: 20px; text-align: center; color: #b3b3b3;">No users found.</td></tr>
<?php else: ?>
<?php foreach ($users as $user): ?>
<tr style="border-bottom: 1px solid #282828;">
<td style="padding: 10px;"><?= htmlspecialchars($user['username']) ?></td>
<td style="padding: 10px;"><?= htmlspecialchars($user['email']) ?></td>
<td style="padding: 10px;"><?= htmlspecialchars($user['role']) ?></td>
<td style="padding: 10px;">
<form action="users.php" method="POST" onsubmit="return confirm('Are you sure you want to delete this user?');" style="display: inline;">
<input type="hidden" name="action" value="delete">
<input type="hidden" name="id" value="<?= $user['id'] ?>">
<button type="submit" class="btn" style="background: none; color: #e76f51;" <?= ($_SESSION['user_id'] == $user['id']) ? 'disabled' : '' ?>><i class="fa fa-trash"></i></button>
</form>
</td>
</tr>
<?php endforeach; ?>
<?php endif; ?>
</tbody>
</table>
</div>
</div>
</main>
</div>
</body>
</html>

504
assets/css/custom.css Normal file
View File

@ -0,0 +1,504 @@
/* assets/css/custom.css */
@import url('https://fonts.googleapis.com/css2?family=Montserrat:wght@400;700&display=swap');
body {
font-family: 'Montserrat', sans-serif;
background-color: #121212;
color: #FFFFFF;
}
.sidebar {
position: fixed;
top: 0;
left: 0;
height: 100vh;
width: 240px;
background-color: #000000;
padding: 24px;
display: flex;
flex-direction: column;
}
.sidebar .nav-link {
color: #b3b3b3;
font-weight: bold;
font-size: 1rem;
padding: 10px 0;
display: flex;
align-items: center;
}
.sidebar .nav-link:hover, .sidebar .nav-link.active {
color: #FFFFFF;
}
.sidebar .nav-link i {
margin-right: 16px;
font-size: 1.5rem;
}
.sidebar .logo {
color: #FFFFFF;
font-weight: bold;
font-size: 1.8rem;
margin-bottom: 30px;
display: flex;
align-items: center;
}
.sidebar .logo i {
color: #1DB954;
margin-right: 10px;
}
.main-content {
margin-left: 240px;
padding: 30px;
background: linear-gradient(to bottom, #222326, #121212 40%);
min-height: 100vh;
}
.main-header h1 {
font-size: 2.5rem;
font-weight: 700;
margin-bottom: 30px;
}
.stat-card, .content-card {
background-color: #181818;
border-radius: 8px;
padding: 20px;
border: 1px solid #282828;
}
.stat-card h5 {
color: #b3b3b3;
font-size: 0.9rem;
margin-bottom: 10px;
}
.stat-card .stat-value {
font-size: 2rem;
font-weight: 700;
}
.content-card h2 {
font-size: 1.5rem;
font-weight: 700;
margin-bottom: 20px;
}
.track-item {
display: flex;
align-items: center;
padding: 10px 0;
border-bottom: 1px solid #282828;
}
.track-item:last-child {
border-bottom: none;
}
.track-item .track-number {
color: #b3b3b3;
width: 30px;
}
.track-item .track-info {
flex-grow: 1;
margin-left: 15px;
}
.track-item .track-title {
font-weight: bold;
}
.track-item .track-artist {
color: #b3b3b3;
font-size: 0.9rem;
}
.track-item .track-plays {
color: #b3b3b3;
min-width: 100px;
text-align: right;
}
.btn-primary-custom {
background-color: #1DB954;
border-color: #1DB954;
color: #FFFFFF;
border-radius: 50px;
font-weight: bold;
padding: 10px 30px;
}
.btn-primary-custom:hover {
background-color: #1ed760;
border-color: #1ed760;
}
/* Upload Form Styles */
.upload-form-container {
background-color: #181818;
border-radius: 8px;
padding: 30px;
max-width: 800px;
margin: auto;
}
.upload-form .form-group {
margin-bottom: 25px;
}
.upload-form label {
display: block;
font-weight: bold;
margin-bottom: 10px;
font-size: 1rem;
}
.upload-form input[type="text"],
.upload-form input[type="file"] {
width: 100%;
padding: 12px 15px;
background-color: #282828;
border: 1px solid #404040;
border-radius: 4px;
color: #FFFFFF;
font-size: 1rem;
}
.upload-form input[type="file"] {
padding: 10px 15px;
}
.upload-form small {
display: block;
color: #b3b3b3;
margin-top: 8px;
font-size: 0.85rem;
}
.upload-form .form-group-checkbox {
display: flex;
align-items: center;
margin-bottom: 25px;
}
.upload-form .form-group-checkbox input[type="checkbox"] {
width: 18px;
height: 18px;
margin-right: 12px;
}
.upload-form .form-group-checkbox label {
margin-bottom: 0;
font-weight: normal;
}
.upload-form .form-group-checkbox a {
color: #1DB954;
text-decoration: none;
}
.upload-form .form-group-checkbox a:hover {
text-decoration: underline;
}
.btn {
padding: 12px 35px;
border-radius: 50px;
font-weight: bold;
font-size: 1rem;
cursor: pointer;
border: none;
text-transform: uppercase;
letter-spacing: 1px;
}
.btn-primary {
background-color: #1DB954;
color: #FFFFFF;
}
.btn-primary:hover {
background-color: #1ed760;
}
.btn-lg {
padding: 15px 45px;
font-size: 1.1rem;
}
.alert {
padding: 15px;
margin-bottom: 20px;
border-radius: 4px;
font-size: 1rem;
}
.alert-success {
background-color: #2a9d8f;
color: #FFFFFF;
}
.alert-danger {
background-color: #e76f51;
color: #FFFFFF;
}
/* Sidebar Refinements from upload.php */
.sidebar-header {
padding-bottom: 20px;
margin-bottom: 10px;
border-bottom: 1px solid #282828;
}
.sidebar .brand {
color: #FFFFFF;
font-weight: bold;
font-size: 1.8rem;
text-decoration: none;
}
.sidebar-nav {
list-style: none;
padding: 0;
margin: 0;
}
.sidebar-nav li a {
color: #b3b3b3;
font-weight: bold;
font-size: 1rem;
padding: 12px 0;
display: flex;
align-items: center;
text-decoration: none;
transition: color 0.2s ease;
}
.sidebar-nav li.active a,
.sidebar-nav li a:hover {
color: #FFFFFF;
}
.sidebar-nav li a i {
margin-right: 16px;
font-size: 1.25rem; /* Slightly smaller icons */
width: 24px; /* Fixed width for alignment */
text-align: center;
}
/* Main Content Refinements */
.page-container {
display: flex;
}
.main-content {
flex-grow: 1;
}
.main-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 30px;
}
.user-profile {
display: flex;
align-items: center;
font-weight: bold;
}
.user-profile .fa-user-circle {
font-size: 2rem;
margin-right: 10px;
}
.content-wrapper {
padding-top: 20px;
}
/* Landing Page Styles */
.landing-container {
max-width: 100%;
margin: 0 auto;
padding: 0;
background-color: #000;
}
.landing-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 20px 50px;
background-color: transparent;
position: absolute;
width: 100%;
top: 0;
left: 0;
z-index: 1000;
}
.landing-header .logo {
font-size: 1.8rem;
font-weight: 700;
color: #FFFFFF;
}
.landing-nav a {
color: #FFFFFF;
text-decoration: none;
margin-left: 25px;
font-weight: bold;
font-size: 0.9rem;
}
.landing-nav .btn {
margin-left: 25px;
padding: 10px 25px;
}
.btn-outline {
background-color: transparent;
border: 1px solid #FFFFFF;
color: #FFFFFF;
}
.btn-outline:hover {
background-color: #FFFFFF;
color: #000000;
}
.hero {
background: linear-gradient(rgba(0,0,0,0.7), rgba(0,0,0,0.7)), url('https://images.pexels.com/photos/1105666/pexels-photo-1105666.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=2') no-repeat center center/cover;
height: 100vh;
display: flex;
align-items: center;
justify-content: center;
text-align: center;
}
.hero-content h1 {
font-size: 4rem;
font-weight: 700;
margin-bottom: 20px;
color: #FFFFFF;
}
.hero-content p {
font-size: 1.2rem;
color: #b3b3b3;
margin-bottom: 40px;
}
.features-section, .pricing-section {
padding: 100px 50px;
text-align: center;
}
.features-section h2, .pricing-section h2 {
font-size: 2.8rem;
font-weight: 700;
margin-bottom: 20px;
}
.features-section p, .pricing-section p {
color: #b3b3b3;
margin-bottom: 60px;
font-size: 1.1rem;
}
.features-grid, .pricing-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 40px;
max-width: 1200px;
margin: 0 auto;
}
.feature-card {
background-color: #181818;
padding: 40px;
border-radius: 8px;
border: 1px solid #282828;
}
.feature-card i {
font-size: 3rem;
color: #1DB954;
margin-bottom: 20px;
}
.feature-card h3 {
font-size: 1.5rem;
font-weight: 700;
margin-bottom: 15px;
}
.feature-card p {
color: #b3b3b3;
font-size: 1rem;
line-height: 1.6;
margin-bottom: 0;
}
.pricing-card {
background-color: #181818;
padding: 40px;
border-radius: 8px;
border: 1px solid #282828;
text-align: left;
}
.pricing-card.recommended {
border-color: #1DB954;
transform: scale(1.05);
}
.pricing-card h3 {
font-size: 1.8rem;
font-weight: 700;
margin-bottom: 10px;
}
.pricing-card .price {
font-size: 1.2rem;
color: #b3b3b3;
margin-bottom: 30px;
}
.pricing-card .price span {
font-size: 3rem;
font-weight: 700;
color: #FFFFFF;
}
.pricing-card ul {
list-style: none;
padding: 0;
margin-bottom: 40px;
}
.pricing-card ul li {
margin-bottom: 15px;
font-size: 1rem;
}
.pricing-card ul li::before {
content: '\2713';
color: #1DB954;
margin-right: 10px;
}
.landing-footer {
text-align: center;
padding: 40px 20px;
background-color: #121212;
border-top: 1px solid #282828;
color: #b3b3b3;
}

29
assets/js/main.js Normal file
View File

@ -0,0 +1,29 @@
// assets/js/main.js
document.addEventListener('DOMContentLoaded', function () {
console.log('Dashboard loaded.');
const addTrackBtn = document.getElementById('add-track-btn');
const tracksContainer = document.getElementById('tracks-container');
let trackIndex = 1;
if (addTrackBtn && tracksContainer) {
addTrackBtn.addEventListener('click', function () {
const trackHtml = `
<div class="track-input-group">
<hr>
<h5>Track ${trackIndex + 1}</h5>
<div class="form-group">
<label for="track-title-${trackIndex}">Track Title</label>
<input type="text" id="track-title-${trackIndex}" name="tracks[${trackIndex}][title]" placeholder="Enter the track title" required>
</div>
<div class="form-group">
<label for="audio-file-${trackIndex}">Audio File</label>
<input type="file" id="audio-file-${trackIndex}" name="tracks[${trackIndex}][audio_file]" accept="audio/mpeg, audio/wav" required>
</div>
</div>
`;
tracksContainer.insertAdjacentHTML('beforeend', trackHtml);
trackIndex++;
});
}
});

14
auth.php Normal file
View File

@ -0,0 +1,14 @@
<?php
session_start();
if (!isset($_SESSION['user_id'])) {
// If no user is logged in, redirect to the login page.
header("Location: login.php");
exit;
}
// Optional: You could add role-based access control here later.
// For example:
// if ($_SESSION['role'] !== 'artist') {
// die("You do not have permission to access this page.");
// }

116
collaborate.php Normal file
View File

@ -0,0 +1,116 @@
<?php
require_once 'auth.php';
require_once 'db/config.php';
$pageTitle = "Collaborate - Zeal Music";
$pdo = db();
$errors = [];
$successMessage = '';
// Handle new post submission
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action']) && $_POST['action'] === 'create') {
$title = trim($_POST['title'] ?? '');
$description = trim($_POST['description'] ?? '');
if (empty($title) || empty($description)) {
$errors[] = "Title and description are required.";
} else {
try {
$stmt = $pdo->prepare("INSERT INTO collaborations (user_id, title, description) VALUES (?, ?, ?)");
$stmt->execute([$_SESSION['user_id'], $title, $description]);
$successMessage = "Your collaboration post has been created!";
} catch (PDOException $e) {
$errors[] = "Database error: " . $e->getMessage();
}
}
}
// Fetch all open collaboration posts
$collaborations = [];
try {
$stmt = $pdo->query("SELECT c.id, c.title, c.description, c.created_at, u.username FROM collaborations c JOIN users u ON c.user_id = u.id WHERE c.status = 'open' ORDER BY c.created_at DESC");
$collaborations = $stmt->fetchAll(PDO::FETCH_ASSOC);
} catch (PDOException $e) {
$errors[] = "Error fetching collaboration posts: " . $e->getMessage();
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title><?php echo htmlspecialchars($pageTitle); ?></title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<link rel="stylesheet" href="assets/css/custom.css?v=<?php echo time(); ?>">
<style>
.collab-post { background-color: #181818; border-radius: 8px; padding: 20px; margin-bottom: 20px; border: 1px solid #282828; }
.collab-post h3 { font-size: 1.3rem; margin-bottom: 10px; color: #1DB954; }
.collab-post .post-meta { font-size: 0.9rem; color: #b3b3b3; margin-bottom: 15px; }
.collab-post p { color: #e0e0e0; }
</style>
</head>
<body class="dark-theme">
<div class="page-container">
<?php include 'partials/sidebar.php'; ?>
<main class="main-content">
<header class="main-header">
<h1>Collaboration Feed</h1>
<div class="user-profile">
<i class="fa fa-user-circle"></i>
<span><?php echo htmlspecialchars($_SESSION['username']); ?></span>
</div>
</header>
<div class="content-wrapper">
<?php if ($successMessage) echo "<div class='alert alert-success'>".htmlspecialchars($successMessage)."</div>"; ?>
<?php foreach ($errors as $error) echo "<div class='alert alert-danger'>".htmlspecialchars($error)."</div>"; ?>
<!-- Create New Post Form -->
<div class="content-card mb-4">
<h2>Create a New Collaboration Post</h2>
<form action="collaborate.php" method="POST" class="upload-form" style="max-width: none;">
<input type="hidden" name="action" value="create">
<div class="form-group">
<label for="title">Title</label>
<input type="text" id="title" name="title" placeholder="E.g., 'Vocalist needed for a pop track'" required>
</div>
<div class="form-group">
<label for="description">Description</label>
<textarea id="description" name="description" rows="4" placeholder="Describe the project, what you're looking for, and include links to demos if you have them." required></textarea>
</div>
<div class="form-group">
<button type="submit" class="btn btn-primary">Post Collaboration</button>
</div>
</form>
</div>
<!-- Collaboration Feed -->
<div class="content-card">
<h2>Open Collaborations</h2>
<?php if (empty($collaborations)):
?>
<p style="text-align: center; color: #b3b3b3; padding: 20px;">No open collaborations at the moment. Why not start one?</p>
<?php else:
foreach ($collaborations as $collab):
?>
<div class="collab-post">
<h3><?php echo htmlspecialchars($collab['title']); ?></h3>
<div class="post-meta">
Posted by <strong><?php echo htmlspecialchars($collab['username']); ?></strong> on <?php echo date("F j, Y", strtotime($collab['created_at'])); ?>
</div>
<p><?php echo nl2br(htmlspecialchars($collab['description'])); ?></p>
</div>
<?php
endforeach;
endif; ?>
</div>
</div>
</main>
</div>
<style>
textarea { width: 100%; padding: 12px 15px; background-color: #282828; border: 1px solid #404040; border-radius: 4px; color: #FFFFFF; font-size: 1rem; font-family: inherit; }
</style>
</body>
</html>

124
dashboard.php Normal file
View File

@ -0,0 +1,124 @@
<?php
require_once 'auth.php';
require_once 'db/config.php'; // Ensure DB config is included
// Fetch tracks for the logged-in user
$user_id = $_SESSION['user_id'];
$tracks = [];
// For admins, show all tracks. For artists, show only their own.
if (isset($_SESSION['role']) && in_array($_SESSION['role'], ['admin', 'super_admin'])) {
$stmt = db()->prepare("SELECT t.*, u.username FROM tracks t JOIN users u ON t.user_id = u.id ORDER BY t.created_at DESC");
if ($stmt->execute()) {
$tracks = $stmt->fetchAll(PDO::FETCH_ASSOC);
}
} else {
$stmt = db()->prepare("SELECT * FROM tracks WHERE user_id = ? ORDER BY created_at DESC");
if ($stmt->execute([$user_id])) {
$tracks = $stmt->fetchAll(PDO::FETCH_ASSOC);
}
}
$pageTitle = "Dashboard - Zeal Music";
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title><?php echo htmlspecialchars($pageTitle); ?></title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<link rel="stylesheet" href="assets/css/custom.css?v=<?php echo time(); ?>">
</head>
<body class="dark-theme">
<div class="page-container">
<?php include 'partials/sidebar.php'; ?>
<!-- Main Content -->
<main class="main-content">
<header class="main-header">
<h1>Welcome Back, <?php echo htmlspecialchars($_SESSION['username']); ?></h1>
<div class="user-profile">
<i class="fa fa-user-circle"></i>
<span><?php echo htmlspecialchars($_SESSION['username']); ?></span>
</div>
</header>
<div class="content-wrapper">
<!-- Key Metrics -->
<div class="row-g-4 mb-4" style="display: flex; gap: 1.5rem;">
<div class="col" style="flex: 1;">
<div class="stat-card">
<h5>Total Plays</h5>
<p class="stat-value">1.2M</p>
</div>
</div>
<div class="col" style="flex: 1;">
<div class="stat-card">
<h5>Listeners (30 days)</h5>
<p class="stat-value">89.4K</p>
</div>
</div>
<div class="col" style="flex: 1;">
<div class="stat-card">
<h5>Engagements</h5>
<p class="stat-value">12.1K</p>
</div>
</div>
</div>
<!-- My Tracks -->
<div class="content-card">
<div class="d-flex justify-content-between align-items-center mb-3">
<h2>My Tracks</h2>
<?php if (!isset($_SESSION['role']) || !in_array($_SESSION['role'], ['admin', 'super_admin'])):
?>
<a href="upload.php" class="btn btn-primary">Upload Music</a>
<?php endif; ?>
</div>
<?php if (empty($tracks)):
?>
<div class="text-center p-4">
<p>No tracks have been uploaded yet.</p>
<?php if (!isset($_SESSION['role']) || !in_array($_SESSION['role'], ['admin', 'super_admin'])):
?>
<a href="upload.php" class="btn btn-primary mt-2">Upload Your First Track</a>
<?php endif; ?>
</div>
<?php else:
?>
<?php foreach ($tracks as $index => $track):
?>
<div class="track-item">
<div class="track-number"><?php echo $index + 1; ?></div>
<img src="<?php echo htmlspecialchars($track['cover_art_path']); ?>" alt="Cover Art" class="track-cover-art-thumb">
<div class="track-info">
<div class="track-title"><?php echo htmlspecialchars($track['title']); ?></div>
<?php
$artistName = '';
if (isset($_SESSION['role']) && in_array($_SESSION['role'], ['admin', 'super_admin'])) {
// For admins, show the username of the uploader
$artistName = $track['username'] ?? 'Unknown Artist';
} else {
// For artists, show their own artist name from the track details
$artistName = $track['artist'] ?? 'Unknown Artist';
}
?>
<div class="track-artist"><?php echo htmlspecialchars($artistName); ?></div>
</div>
<div class="track-album"><?php echo htmlspecialchars($track['album_title']); ?></div>
<div class="track-plays">0 plays</div>
</div>
<?php endforeach; ?>
<?php endif; ?>
</div>
</div>
</main>
</div>
<script src="assets/js/main.js?v=<?php echo time(); ?>"></script>
</body>
</html>

34
db/migrate.php Normal file
View File

@ -0,0 +1,34 @@
<?php
// Simple migration script
require_once __DIR__ . '/config.php';
function run_migrations() {
try {
$pdo = db();
$migrationsDir = __DIR__ . '/migrations';
if (!is_dir($migrationsDir)) {
echo "Migrations directory not found.\n";
return;
}
$files = glob($migrationsDir . '/*.sql');
sort($files);
foreach ($files as $file) {
echo "Running migration: " . basename($file) . "...\n";
$sql = file_get_contents($file);
$pdo->exec($sql);
echo "Success.\n";
}
echo "\nAll migrations completed.\n";
} catch (PDOException $e) {
die("Database migration failed: " . $e->getMessage() . "\n");
}
}
run_migrations();

View File

@ -0,0 +1,10 @@
-- 001_create_users_table.sql
CREATE TABLE IF NOT EXISTS `users` (
`id` INT AUTO_INCREMENT PRIMARY KEY,
`username` VARCHAR(50) NOT NULL UNIQUE,
`email` VARCHAR(100) NOT NULL UNIQUE,
`password` VARCHAR(255) NOT NULL,
`role` ENUM('artist', 'publisher', 'admin', 'super_admin') NOT NULL DEFAULT 'artist',
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

View File

@ -0,0 +1,10 @@
-- 002_create_stations_table.sql
CREATE TABLE IF NOT EXISTS `stations` (
`id` INT AUTO_INCREMENT PRIMARY KEY,
`name` VARCHAR(100) NOT NULL,
`contact_email` VARCHAR(100) NOT NULL UNIQUE,
`genre_focus` VARCHAR(255) NULL,
`website` VARCHAR(255) NULL,
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

View File

@ -0,0 +1,11 @@
-- 003_create_collaborations_table.sql
CREATE TABLE IF NOT EXISTS `collaborations` (
`id` INT AUTO_INCREMENT PRIMARY KEY,
`user_id` INT NOT NULL,
`title` VARCHAR(255) NOT NULL,
`description` TEXT NOT NULL,
`status` ENUM('open', 'closed') NOT NULL DEFAULT 'open',
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

View File

@ -0,0 +1,12 @@
CREATE TABLE IF NOT EXISTS tracks (
id INT AUTO_INCREMENT PRIMARY KEY,
user_id INT NOT NULL,
title VARCHAR(255) NOT NULL,
album VARCHAR(255),
genre VARCHAR(100),
release_date DATE,
file_path VARCHAR(255) NOT NULL,
cover_art_path VARCHAR(255),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
);

View File

@ -0,0 +1,8 @@
CREATE TABLE IF NOT EXISTS packages (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(255) NOT NULL,
price DECIMAL(10, 2) NOT NULL,
features TEXT NOT NULL,
is_recommended BOOLEAN DEFAULT FALSE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

View File

@ -0,0 +1,8 @@
CREATE TABLE IF NOT EXISTS monitoring_data (
id INT AUTO_INCREMENT PRIMARY KEY,
track_id INT NOT NULL,
plays INT NOT NULL,
date DATE NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (track_id) REFERENCES tracks(id) ON DELETE CASCADE
);

View File

@ -0,0 +1 @@
ALTER TABLE users ADD COLUMN package_id INT NULL;

154
index.php
View File

@ -1,150 +1,8 @@
<?php <?php
declare(strict_types=1); require_once 'auth.php';
@ini_set('display_errors', '1');
@error_reporting(E_ALL);
@date_default_timezone_set('UTC');
$phpVersion = PHP_VERSION; if (is_logged_in()) {
$now = date('Y-m-d H:i:s'); require 'dashboard.php';
?> } else {
<!doctype html> require 'landing.php';
<html lang="en"> }
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>New Style</title>
<?php
// Read project preview data from environment
$projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? '';
$projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
?>
<?php if ($projectDescription): ?>
<!-- Meta description -->
<meta name="description" content='<?= htmlspecialchars($projectDescription) ?>' />
<!-- Open Graph meta tags -->
<meta property="og:description" content="<?= htmlspecialchars($projectDescription) ?>" />
<!-- Twitter meta tags -->
<meta property="twitter:description" content="<?= htmlspecialchars($projectDescription) ?>" />
<?php endif; ?>
<?php if ($projectImageUrl): ?>
<!-- Open Graph image -->
<meta property="og:image" content="<?= htmlspecialchars($projectImageUrl) ?>" />
<!-- Twitter image -->
<meta property="twitter:image" content="<?= htmlspecialchars($projectImageUrl) ?>" />
<?php endif; ?>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;700&display=swap" rel="stylesheet">
<style>
:root {
--bg-color-start: #6a11cb;
--bg-color-end: #2575fc;
--text-color: #ffffff;
--card-bg-color: rgba(255, 255, 255, 0.01);
--card-border-color: rgba(255, 255, 255, 0.1);
}
body {
margin: 0;
font-family: 'Inter', sans-serif;
background: linear-gradient(45deg, var(--bg-color-start), var(--bg-color-end));
color: var(--text-color);
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
text-align: center;
overflow: hidden;
position: relative;
}
body::before {
content: '';
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-image: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100" viewBox="0 0 100 100"><path d="M-10 10L110 10M10 -10L10 110" stroke-width="1" stroke="rgba(255,255,255,0.05)"/></svg>');
animation: bg-pan 20s linear infinite;
z-index: -1;
}
@keyframes bg-pan {
0% { background-position: 0% 0%; }
100% { background-position: 100% 100%; }
}
main {
padding: 2rem;
}
.card {
background: var(--card-bg-color);
border: 1px solid var(--card-border-color);
border-radius: 16px;
padding: 2rem;
backdrop-filter: blur(20px);
-webkit-backdrop-filter: blur(20px);
box-shadow: 0 8px 32px 0 rgba(0, 0, 0, 0.1);
}
.loader {
margin: 1.25rem auto 1.25rem;
width: 48px;
height: 48px;
border: 3px solid rgba(255, 255, 255, 0.25);
border-top-color: #fff;
border-radius: 50%;
animation: spin 1s linear infinite;
}
@keyframes spin {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
.hint {
opacity: 0.9;
}
.sr-only {
position: absolute;
width: 1px; height: 1px;
padding: 0; margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap; border: 0;
}
h1 {
font-size: 3rem;
font-weight: 700;
margin: 0 0 1rem;
letter-spacing: -1px;
}
p {
margin: 0.5rem 0;
font-size: 1.1rem;
}
code {
background: rgba(0,0,0,0.2);
padding: 2px 6px;
border-radius: 4px;
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
}
footer {
position: absolute;
bottom: 1rem;
font-size: 0.8rem;
opacity: 0.7;
}
</style>
</head>
<body>
<main>
<div class="card">
<h1>Analyzing your requirements and generating your website…</h1>
<div class="loader" role="status" aria-live="polite" aria-label="Applying initial changes">
<span class="sr-only">Loading…</span>
</div>
<p class="hint"><?= ($_SERVER['HTTP_HOST'] ?? '') === 'appwizzy.com' ? 'AppWizzy' : 'Flatlogic' ?> AI is collecting your requirements and applying the first changes.</p>
<p class="hint">This page will update automatically as the plan is implemented.</p>
<p>Runtime: PHP <code><?= htmlspecialchars($phpVersion) ?></code> — UTC <code><?= htmlspecialchars($now) ?></code></p>
</div>
</main>
<footer>
Page updated: <?= htmlspecialchars($now) ?> (UTC)
</footer>
</body>
</html>

89
landing.php Normal file
View File

@ -0,0 +1,89 @@
<?php
require_once 'db/config.php';
// Fetch all packages
$stmt = db()->query("SELECT * FROM packages ORDER BY price");
$packages = $stmt->fetchAll(PDO::FETCH_ASSOC);
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Zeal Music - Unleash Your Music's Potential</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<link rel="stylesheet" href="assets/css/custom.css?v=<?php echo time(); ?>">
</head>
<body class="dark-theme">
<div class="landing-container">
<header class="landing-header">
<div class="logo">Zeal Music</div>
<nav class="landing-nav">
<a href="#features">Features</a>
<a href="#pricing">Pricing</a>
<a href="login.php" class="btn btn-outline">Login</a>
<a href="register.php" class="btn btn-primary">Sign Up</a>
</nav>
</header>
<main>
<section class="hero">
<div class="hero-content">
<h1>Unleash Your Music's Potential</h1>
<p>The ultimate platform for artists to manage, distribute, and monetize their work.</p>
<a href="register.php" class="btn btn-primary btn-lg">Get Started Now</a>
</div>
</section>
<section id="features" class="features-section">
<h2>Why Zeal Music?</h2>
<div class="features-grid">
<div class="feature-card">
<i class="fas fa-chart-line"></i>
<h3>Detailed Analytics</h3>
<p>Gain deep insights into your listeners and track performance across all platforms.</p>
</div>
<div class="feature-card">
<i class="fas fa-upload"></i>
<h3>Direct Uploads</h3>
<p>Easily upload your tracks, albums, and cover art with our streamlined process.</p>
</div>
<div class="feature-card">
<i class="fas fa-globe"></i>
<h3>Global Reach</h3>
<p>Distribute your music to major streaming services and digital stores worldwide.</p>
</div>
</div>
</section>
<section id="pricing" class="pricing-section">
<h2>Choose Your Plan</h2>
<p>Simple, transparent pricing. No hidden fees.</p>
<div class="pricing-grid">
<?php foreach ($packages as $package): ?>
<div class="pricing-card <?php echo $package['is_recommended'] ? 'recommended' : ''; ?>">
<h3><?php echo htmlspecialchars($package['name']); ?></h3>
<p class="price"><span>$<?php echo htmlspecialchars($package['price']); ?></span>/month</p>
<ul>
<?php
$features = explode(',', $package['features']);
foreach ($features as $feature):
?>
<li><?php echo htmlspecialchars(trim($feature)); ?></li>
<?php endforeach; ?>
</ul>
<a href="register.php" class="btn btn-primary">Select Plan</a>
</div>
<?php endforeach; ?>
</div>
</section>
</main>
<footer class="landing-footer">
<p>&copy; <?php echo date("Y"); ?> Zeal Music. All Rights Reserved.</p>
</footer>
</div>
<script src="assets/js/main.js?v=<?php echo time(); ?>"></script>
</body>
</html>

101
login.php Normal file
View File

@ -0,0 +1,101 @@
<?php
session_start();
require_once __DIR__ . '/db/config.php';
$pageTitle = "Login - Zeal Music";
$errors = [];
// If user is already logged in, redirect to dashboard
if (isset($_SESSION['user_id'])) {
header("Location: index.php");
exit;
}
if ($_SERVER["REQUEST_METHOD"] == "POST") {
$email = trim($_POST['email'] ?? '');
$password = $_POST['password'] ?? '';
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
$errors[] = 'Invalid email format.';
}
if (empty($password)) {
$errors[] = 'Password is required.';
}
if (empty($errors)) {
try {
$pdo = db();
$stmt = $pdo->prepare("SELECT * FROM users WHERE email = :email");
$stmt->execute(['email' => $email]);
$user = $stmt->fetch(PDO::FETCH_ASSOC);
if ($user && password_verify($password, $user['password'])) {
// Password is correct, start session
$_SESSION['user_id'] = $user['id'];
$_SESSION['username'] = $user['username'];
$_SESSION['role'] = $user['role'];
header("Location: index.php");
exit;
} else {
$errors[] = 'Invalid email or password.';
}
} catch (PDOException $e) {
$errors[] = "Database error: " . $e->getMessage();
}
}
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title><?php echo htmlspecialchars($pageTitle); ?></title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<link rel="stylesheet" href="assets/css/custom.css?v=<?php echo time(); ?>">
</head>
<body class="dark-theme">
<div class="page-container">
<nav class="sidebar">
<div class="sidebar-header">
<a href="index.php" class="brand">Zeal Music</a>
</div>
</nav>
<main class="main-content">
<div class="content-wrapper">
<div class="upload-form-container">
<h2>Log In to Your Account</h2>
<br>
<?php if (!empty($errors)):
foreach ($errors as $error):
?>
<div class="alert alert-danger"><?php echo htmlspecialchars($error); ?></div>
<?php
endforeach;
endif; ?>
<form action="login.php" method="POST" class="upload-form">
<div class="form-group">
<label for="email">Email Address</label>
<input type="email" id="email" name="email" placeholder="Your email" required>
</div>
<div class="form-group">
<label for="password">Password</label>
<input type="password" id="password" name="password" placeholder="Your password" required>
</div>
<div class="form-group">
<button type="submit" class="btn btn-primary btn-lg">Log In</button>
</div>
<p style="text-align: center; margin-top: 20px;">
Don't have an account? <a href="register.php" style="color: #1DB954;">Register Now</a>
</p>
</form>
</div>
</div>
</main>
</div>
</body>
</html>

6
logout.php Normal file
View File

@ -0,0 +1,6 @@
<?php
session_start();
session_unset();
session_destroy();
header("Location: login.php");
exit;

38
partials/sidebar.php Normal file
View File

@ -0,0 +1,38 @@
<?php
// partials/sidebar.php
// Ensure session is started.
if (session_status() === PHP_SESSION_NONE) {
session_start();
}
?>
<nav class="sidebar">
<div class="sidebar-header">
<a href="/index.php" class="brand">Zeal Music</a>
</div>
<ul class="sidebar-nav">
<li class="<?php echo (basename($_SERVER['PHP_SELF']) == 'index.php') ? 'active' : ''; ?>"><a href="/index.php"><i class="fa fa-home"></i> Home</a></li>
<li><a href="#"><i class="fa fa-music"></i> Tracks</a></li>
<li><a href="#"><i class="fa fa-chart-line"></i> Analytics</a></li>
<li class="<?php echo (basename($_SERVER['PHP_SELF']) == 'collaborate.php') ? 'active' : ''; ?>"><a href="/collaborate.php"><i class="fa fa-users"></i> Collaborate</a></li>
<?php // Show non-admin links
if (!isset($_SESSION['role']) || !in_array($_SESSION['role'], ['admin', 'super_admin'])):
?>
<li class="<?php echo (basename($_SERVER['PHP_SELF']) == 'upload.php') ? 'active' : ''; ?>"><a href="/upload.php"><i class="fa fa-upload"></i> Upload Music</a></li>
<?php endif; ?>
<?php // Show Admin links only to admins
if (isset($_SESSION['role']) && in_array($_SESSION['role'], ['admin', 'super_admin'])):
?>
<li class="<?php echo (basename($_SERVER['PHP_SELF']) == 'stations.php') ? 'active' : ''; ?>"><a href="/admin/stations.php"><i class="fa fa-broadcast-tower"></i> Manage Stations</a></li>
<li class="<?php echo (basename($_SERVER['PHP_SELF']) == 'users.php') ? 'active' : ''; ?>"><a href="/admin/users.php"><i class="fa fa-users-cog"></i> Manage Users</a></li>
<li class="<?php echo (basename($_SERVER['PHP_SELF']) == 'monitoring.php') ? 'active' : ''; ?>"><a href="/admin/monitoring.php"><i class="fa fa-file-csv"></i> Music Monitoring</a></li>
<li class="<?php echo (basename($_SERVER['PHP_SELF']) == 'packages.php') ? 'active' : ''; ?>"><a href="/admin/packages.php"><i class="fa fa-box"></i> Manage Packages</a></li>
<?php endif; ?>
</ul>
<div style="margin-top: auto; padding-top: 20px; border-top: 1px solid #282828;">
<ul class="sidebar-nav">
<li><a href="/logout.php"><i class="fa fa-sign-out-alt"></i> Logout</a></li>
</ul>
</div>
</nav>

143
register.php Normal file
View File

@ -0,0 +1,143 @@
<?php
require_once __DIR__ . '/db/config.php';
$pageTitle = "Register - Zeal Music";
$errors = [];
$successMessage = "";
// Fetch packages from the database
try {
$pdo = db();
$stmt = $pdo->query("SELECT id, name, price, features FROM packages ORDER BY price");
$packages = $stmt->fetchAll(PDO::FETCH_ASSOC);
} catch (PDOException $e) {
$errors[] = "Database error: " . $e->getMessage();
$packages = [];
}
if ($_SERVER["REQUEST_METHOD"] == "POST") {
$username = trim($_POST['username'] ?? '');
$email = trim($_POST['email'] ?? '');
$password = $_POST['password'] ?? '';
$password_confirm = $_POST['password_confirm'] ?? '';
$package_id = $_POST['package_id'] ?? '';
// Validation
if (empty($username)) $errors[] = 'Username is required.';
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) $errors[] = 'A valid email is required.';
if (strlen($password) < 8) $errors[] = 'Password must be at least 8 characters long.';
if ($password !== $password_confirm) $errors[] = 'Passwords do not match.';
if (empty($package_id)) $errors[] = 'Please select a package.';
// Check if user already exists
if (empty($errors)) {
try {
$pdo = db();
$stmt = $pdo->prepare("SELECT id FROM users WHERE username = :username OR email = :email");
$stmt->execute(['username' => $username, 'email' => $email]);
if ($stmt->fetch()) {
$errors[] = 'Username or email already exists.';
}
} catch (PDOException $e) {
$errors[] = "Database error: " . $e->getMessage();
}
}
// Insert new user
if (empty($errors)) {
$hashed_password = password_hash($password, PASSWORD_DEFAULT);
try {
$pdo = db();
$stmt = $pdo->prepare("INSERT INTO users (username, email, password, role, package_id) VALUES (:username, :email, :password, 'artist', :package_id)");
$stmt->execute(['username' => $username, 'email' => $email, 'password' => $hashed_password, 'package_id' => $package_id]);
$successMessage = "Registration successful! You can now <a href='login.php'>log in</a>.";
} catch (PDOException $e) {
$errors[] = "Database error during registration: " . $e->getMessage();
}
}
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title><?php echo htmlspecialchars($pageTitle); ?></title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<link rel="stylesheet" href="assets/css/custom.css?v=<?php echo time(); ?>">
</head>
<body class="dark-theme">
<div class="page-container">
<!-- Simplified Sidebar for Auth Pages -->
<nav class="sidebar">
<div class="sidebar-header">
<a href="index.php" class="brand">Zeal Music</a>
</div>
</nav>
<!-- Main Content -->
<main class="main-content">
<div class="content-wrapper">
<div class="upload-form-container">
<h2>Create Your Artist Account</h2>
<br>
<?php if (!empty($errors)):
foreach ($errors as $error):
?>
<div class="alert alert-danger"><?php echo htmlspecialchars($error); ?></div>
<?php endforeach;
endif; ?>
<?php if ($successMessage): ?>
<div class="alert alert-success"><?php echo $successMessage; ?></div>
<?php else: ?>
<form action="register.php" method="POST" class="upload-form">
<div class="form-group">
<label for="username">Username</label>
<input type="text" id="username" name="username" placeholder="Your unique username" required value="<?php echo htmlspecialchars($username ?? ''); ?>">
</div>
<div class="form-group">
<label for="email">Email Address</label>
<input type="email" id="email" name="email" placeholder="Your contact email" required value="<?php echo htmlspecialchars($email ?? ''); ?>">
</div>
<div class="form-group">
<label for="password">Password</label>
<input type="password" id="password" name="password" placeholder="Minimum 8 characters" required>
</div>
<div class="form-group">
<label for="password_confirm">Confirm Password</label>
<input type="password" id="password_confirm" name="password_confirm" placeholder="Enter your password again" required>
</div>
<div class="form-group">
<label>Select a Package</label>
<div class="package-selection">
<?php foreach ($packages as $package): ?>
<div class="package-option">
<input type="radio" id="package_<?php echo $package['id']; ?>" name="package_id" value="<?php echo $package['id']; ?>" required>
<label for="package_<?php echo $package['id']; ?>">
<strong><?php echo htmlspecialchars($package['name']); ?></strong> - $<?php echo htmlspecialchars($package['price']); ?>/month
<p><?php echo htmlspecialchars($package['features']); ?></p>
</label>
</div>
<?php endforeach; ?>
</div>
</div>
<div class="form-group">
<button type="submit" class="btn btn-primary btn-lg">Register</button>
</div>
<p style="text-align: center; margin-top: 20px;">
Already have an account? <a href="login.php" style="color: #1DB954;">Log In</a>
</p>
</form>
<?php endif; ?>
</div>
</div>
</main>
</div>
</body>
</html>

190
upload.php Normal file
View File

@ -0,0 +1,190 @@
<?php
require_once 'auth.php';
require_once 'db/config.php';
$pageTitle = "Upload Album - Zeal Music";
$successMessage = "";
$errorMessage = "";
if ($_SERVER["REQUEST_METHOD"] == "POST") {
$pdo = db();
$userId = $_SESSION['user_id'];
// Album details
$artistName = trim($_POST['artist-name'] ?? 'Unknown Artist');
$albumName = trim($_POST['album-name'] ?? 'Untitled Album');
$genre = trim($_POST['genre'] ?? '');
$releaseDate = !empty($_POST['release-date']) ? $_POST['release-date'] : null;
// Handle Album Art Upload
$coverArtPath = null;
if (isset($_FILES['album-art']) && $_FILES['album-art']['error'] == UPLOAD_ERR_OK) {
$artTmpName = $_FILES['album-art']['tmp_name'];
$artName = basename($_FILES['album-art']['name']);
$artExt = pathinfo($artName, PATHINFO_EXTENSION);
$newArtName = uniqid('art_', true) . '.' . $artExt;
$coverArtPath = 'uploads/cover_art/' . $newArtName;
if (!move_uploaded_file($artTmpName, $coverArtPath)) {
$errorMessage = "Failed to upload album art.";
$coverArtPath = null;
}
}
// Handle Tracks
if (empty($errorMessage) && isset($_POST['tracks']) && is_array($_POST['tracks'])) {
$tracks = $_POST['tracks'];
$files = $_FILES['tracks'];
$allSucceeded = true;
foreach ($tracks as $index => $track) {
$trackTitle = trim($track['title']);
// Restructure FILES array for easier access
$audioFile = [
'name' => $files['name']['audio_file'][$index],
'type' => $files['type']['audio_file'][$index],
'tmp_name' => $files['tmp_name']['audio_file'][$index],
'error' => $files['error']['audio_file'][$index],
'size' => $files['size']['audio_file'][$index]
];
if (!empty($trackTitle) && $audioFile['error'] == UPLOAD_ERR_OK) {
$audioTmpName = $audioFile['tmp_name'];
$audioName = basename($audioFile['name']);
$audioExt = pathinfo($audioName, PATHINFO_EXTENSION);
$newAudioName = uniqid('track_', true) . '.' . $audioExt;
$audioFilePath = 'uploads/music/' . $newAudioName;
if (move_uploaded_file($audioTmpName, $audioFilePath)) {
try {
$stmt = $pdo->prepare(
"INSERT INTO tracks (user_id, title, album, genre, release_date, file_path, cover_art_path)
VALUES (:user_id, :title, :album, :genre, :release_date, :file_path, :cover_art_path)"
);
$stmt->execute([
':user_id' => $userId,
':title' => $trackTitle,
':album' => $albumName,
':genre' => $genre,
':release_date' => $releaseDate,
':file_path' => $audioFilePath,
':cover_art_path' => $coverArtPath
]);
} catch (PDOException $e) {
$errorMessage = "Database error: " . $e->getMessage();
$allSucceeded = false;
break; // Exit loop on DB error
}
} else {
$errorMessage = "Failed to move uploaded audio file for track: " . htmlspecialchars($trackTitle);
$allSucceeded = false;
break;
}
} else {
$errorMessage = "Please provide a title and audio file for all tracks. Error code: " . $audioFile['error'];
$allSucceeded = false;
break;
}
}
if ($allSucceeded) {
$successMessage = "Album and all tracks submitted successfully!";
}
} elseif (empty($errorMessage)) {
$errorMessage = "No tracks were submitted. Please add at least one track.";
}
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title><?php echo htmlspecialchars($pageTitle); ?></title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<link rel="stylesheet" href="assets/css/custom.css?v=<?php echo time(); ?>">
</head>
<body class="dark-theme">
<div class="page-container">
<?php include 'partials/sidebar.php'; ?>
<main class="main-content">
<header class="main-header">
<h1>Upload Your Album</h1>
<div class="user-profile">
<i class="fa fa-user-circle"></i>
<span><?php echo htmlspecialchars($_SESSION['username']); ?></span>
</div>
</header>
<div class="content-wrapper">
<div class="upload-form-container">
<?php if ($successMessage): ?>
<div class="alert alert-success"><?php echo htmlspecialchars($successMessage); ?></div>
<?php endif; ?>
<?php if ($errorMessage): ?>
<div class="alert alert-danger"><?php echo htmlspecialchars($errorMessage); ?></div>
<?php endif; ?>
<form action="upload.php" method="POST" enctype="multipart/form-data" class="upload-form">
<fieldset>
<legend>Album Details</legend>
<div class="form-group">
<label for="artist-name">Artist Name</label>
<input type="text" id="artist-name" name="artist-name" value="<?php echo htmlspecialchars($_SESSION['username']); ?>" required>
</div>
<div class="form-group">
<label for="album-name">Album Name</label>
<input type="text" id="album-name" name="album-name" placeholder="Enter the album name" required>
</div>
<div class="form-group">
<label for="album-art">Album Art</label>
<input type="file" id="album-art" name="album-art" accept="image/png, image/jpeg">
<small>Recommended: Square image, 1500x1500px. Applies to all tracks.</small>
</div>
<div class="form-group">
<label for="genre">Genre</label>
<input type="text" id="genre" name="genre" placeholder="e.g., Pop, Rock, Hip-Hop">
</div>
<div class="form-group">
<label for="release-date">Release Date</label>
<input type="date" id="release-date" name="release-date">
</div>
</fieldset>
<fieldset>
<legend>Tracks</legend>
<div id="tracks-container">
<div class="track-input-group">
<h5>Track 1</h5>
<div class="form-group">
<label for="track-title-0">Track Title</label>
<input type="text" id="track-title-0" name="tracks[0][title]" placeholder="Enter the track title" required>
</div>
<div class="form-group">
<label for="audio-file-0">Audio File</label>
<input type="file" id="audio-file-0" name="tracks[0][audio_file]" accept="audio/mpeg, audio/wav" required>
<small>Required: High-quality MP3 or WAV file.</small>
</div>
</div>
</div>
<button type="button" id="add-track-btn" class="btn btn-secondary">Add Another Track</button>
</fieldset>
<div class="form-group-checkbox">
<input type="checkbox" id="copyright-agree" name="copyright-agree" required>
<label for="copyright-agree">I confirm that I own all the rights to this music and agree to the <a href="#">distribution terms</a>.</label>
</div>
<div class="form-group">
<button type="submit" class="btn btn-primary btn-lg">Submit Album</button>
</div>
</form>
</div>
</div>
</main>
</div>
<script src="assets/js/main.js?v=<?php echo time(); ?>"></script>
</body>
</html>