Compare commits
No commits in common. "ai-dev" and "master" have entirely different histories.
@ -1,59 +0,0 @@
|
|||||||
<?php
|
|
||||||
header('Content-Type: application/json');
|
|
||||||
require_once __DIR__ . '/../db/config.php';
|
|
||||||
|
|
||||||
// Get the posted data
|
|
||||||
$data = json_decode(file_get_contents('php://input'), true);
|
|
||||||
|
|
||||||
if (!$data || !isset($data['lineup_id']) || !isset($data['song_id'])) {
|
|
||||||
http_response_code(400);
|
|
||||||
echo json_encode(['success' => false, 'message' => 'Invalid input.']);
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
$lineup_id = $data['lineup_id'];
|
|
||||||
$song_id = $data['song_id'];
|
|
||||||
|
|
||||||
try {
|
|
||||||
$pdo = db();
|
|
||||||
|
|
||||||
// Check if the song is already in the lineup
|
|
||||||
$stmt = $pdo->prepare("SELECT 1 FROM lineup_songs WHERE lineup_id = ? AND song_id = ?");
|
|
||||||
$stmt->execute([$lineup_id, $song_id]);
|
|
||||||
if ($stmt->fetch()) {
|
|
||||||
http_response_code(409); // 409 Conflict
|
|
||||||
echo json_encode(['success' => false, 'message' => 'השיר כבר קיים בליינאפ.']);
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the current number of songs in the lineup to determine the new order.
|
|
||||||
// This is much faster than calculating MAX(song_order).
|
|
||||||
$stmt = $pdo->prepare("SELECT COUNT(*) as song_count FROM lineup_songs WHERE lineup_id = ?");
|
|
||||||
$stmt->execute([$lineup_id]);
|
|
||||||
$result = $stmt->fetch(PDO::FETCH_ASSOC);
|
|
||||||
$new_order = $result ? (int)$result['song_count'] : 0;
|
|
||||||
|
|
||||||
|
|
||||||
// Insert the new song
|
|
||||||
$stmt = $pdo->prepare("INSERT INTO lineup_songs (lineup_id, song_id, song_order) VALUES (?, ?, ?)");
|
|
||||||
|
|
||||||
if ($stmt->execute([$lineup_id, $song_id, $new_order])) {
|
|
||||||
// Fetch the added song details to return to the client
|
|
||||||
$song_stmt = $pdo->prepare("SELECT * FROM songs WHERE id = ?");
|
|
||||||
$song_stmt->execute([$song_id]);
|
|
||||||
$song = $song_stmt->fetch(PDO::FETCH_ASSOC);
|
|
||||||
|
|
||||||
echo json_encode(['success' => true, 'message' => 'השיר נוסף בהצלחה!', 'song' => $song]);
|
|
||||||
} else {
|
|
||||||
http_response_code(500);
|
|
||||||
echo json_encode(['success' => false, 'message' => 'לא ניתן היה להוסיף את השיר.']);
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (PDOException $e) {
|
|
||||||
// Log error to a file for debugging
|
|
||||||
error_log("Add song to lineup failed: " . $e->getMessage());
|
|
||||||
http_response_code(500);
|
|
||||||
// Return a more specific error message for debugging
|
|
||||||
echo json_encode(['success' => false, 'message' => 'שגיאת שרת: ' . $e->getMessage()]);
|
|
||||||
}
|
|
||||||
?>
|
|
||||||
@ -1,57 +0,0 @@
|
|||||||
<?php
|
|
||||||
require_once __DIR__ . '/../db/config.php';
|
|
||||||
header('Content-Type: application/json');
|
|
||||||
|
|
||||||
$response = ['success' => false, 'error' => 'Invalid request'];
|
|
||||||
|
|
||||||
if ($_SERVER['REQUEST_METHOD'] === 'GET') {
|
|
||||||
if (isset($_GET['lineup_id']) && is_numeric($_GET['lineup_id'])) {
|
|
||||||
try {
|
|
||||||
$lineup_id = intval($_GET['lineup_id']);
|
|
||||||
$pdo = db();
|
|
||||||
$stmt = $pdo->prepare("
|
|
||||||
SELECT s.*, ls.song_order FROM songs s
|
|
||||||
JOIN lineup_songs ls ON s.id = ls.song_id
|
|
||||||
WHERE ls.lineup_id = ?
|
|
||||||
ORDER BY ls.song_order ASC
|
|
||||||
");
|
|
||||||
$stmt->execute([$lineup_id]);
|
|
||||||
$songs = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
|
||||||
echo json_encode($songs);
|
|
||||||
exit;
|
|
||||||
} catch (PDOException $e) {
|
|
||||||
$response['error'] = 'Database error: ' . $e->getMessage();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$response['error'] = 'Lineup ID is required.';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|
||||||
$data = json_decode(file_get_contents('php://input'), true);
|
|
||||||
|
|
||||||
if (isset($data['action'])) {
|
|
||||||
switch ($data['action']) {
|
|
||||||
case 'create':
|
|
||||||
if (!empty($data['name'])) {
|
|
||||||
try {
|
|
||||||
$pdo = db();
|
|
||||||
$stmt = $pdo->prepare("INSERT INTO lineups (name) VALUES (?)");
|
|
||||||
$stmt->execute([$data['name']]);
|
|
||||||
$response = ['success' => true, 'lineup_id' => $pdo->lastInsertId()];
|
|
||||||
} catch (PDOException $e) {
|
|
||||||
$response['error'] = 'Database error: ' . $e->getMessage();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$response['error'] = 'Lineup name is required.';
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
$response['error'] = 'Unknown action.';
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
echo json_encode($response);
|
|
||||||
@ -1,28 +0,0 @@
|
|||||||
<?php
|
|
||||||
require_once '../db/config.php';
|
|
||||||
|
|
||||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|
||||||
$data = json_decode(file_get_contents('php://input'), true);
|
|
||||||
$lineup_id = $data['lineup_id'] ?? null;
|
|
||||||
$song_id = $data['song_id'] ?? null;
|
|
||||||
|
|
||||||
if ($lineup_id && $song_id) {
|
|
||||||
try {
|
|
||||||
$pdo = db();
|
|
||||||
$stmt = $pdo->prepare("DELETE FROM lineup_songs WHERE lineup_id = ? AND song_id = ?");
|
|
||||||
$stmt->execute([$lineup_id, $song_id]);
|
|
||||||
|
|
||||||
echo json_encode(['success' => true]);
|
|
||||||
} catch (PDOException $e) {
|
|
||||||
http_response_code(500);
|
|
||||||
echo json_encode(['success' => false, 'message' => 'Database error: ' . $e->getMessage()]);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
http_response_code(400);
|
|
||||||
echo json_encode(['success' => false, 'message' => 'Invalid input.']);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
http_response_code(405);
|
|
||||||
echo json_encode(['success' => false, 'message' => 'Method not allowed.']);
|
|
||||||
}
|
|
||||||
?>
|
|
||||||
@ -1,23 +0,0 @@
|
|||||||
<?php
|
|
||||||
require_once '../db/config.php';
|
|
||||||
|
|
||||||
// --- Logic ---
|
|
||||||
$pdo = db();
|
|
||||||
|
|
||||||
// Fetch songs with search functionality
|
|
||||||
$search = $_GET['q'] ?? $_GET['search'] ?? '';
|
|
||||||
$sql = "SELECT * FROM songs";
|
|
||||||
$params = [];
|
|
||||||
if (!empty($search)) {
|
|
||||||
$sql .= " WHERE name LIKE ? OR artist LIKE ? OR bpm LIKE ? OR song_key LIKE ? OR notes LIKE ? OR tags LIKE ?";
|
|
||||||
$searchTerm = "%{$search}%";
|
|
||||||
$params = [$searchTerm, $searchTerm, $searchTerm, $searchTerm, $searchTerm, $searchTerm];
|
|
||||||
}
|
|
||||||
$sql .= " ORDER BY id ASC";
|
|
||||||
$stmt = $pdo->prepare($sql);
|
|
||||||
$stmt->execute($params);
|
|
||||||
$songs = $stmt->fetchAll();
|
|
||||||
|
|
||||||
// --- Presentation ---
|
|
||||||
header('Content-Type: application/json');
|
|
||||||
echo json_encode($songs);
|
|
||||||
@ -1,53 +0,0 @@
|
|||||||
body {
|
|
||||||
font-family: 'Heebo', sans-serif;
|
|
||||||
background-color: #F8FAFC;
|
|
||||||
}
|
|
||||||
|
|
||||||
.navbar-brand {
|
|
||||||
font-weight: 700;
|
|
||||||
font-size: 1.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-primary {
|
|
||||||
background-color: #1D4ED8;
|
|
||||||
border-color: #1D4ED8;
|
|
||||||
}
|
|
||||||
.btn-primary:hover {
|
|
||||||
background-color: #1E40AF;
|
|
||||||
border-color: #1E40AF;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-secondary {
|
|
||||||
background-color: #F97316;
|
|
||||||
border-color: #F97316;
|
|
||||||
}
|
|
||||||
.btn-secondary:hover {
|
|
||||||
background-color: #EA580C;
|
|
||||||
border-color: #EA580C;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card {
|
|
||||||
border-radius: 0.5rem;
|
|
||||||
border: 1px solid #e2e8f0;
|
|
||||||
box-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.05), 0 1px 2px -1px rgb(0 0 0 / 0.05);
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-control, .form-select {
|
|
||||||
border-radius: 0.5rem;
|
|
||||||
}
|
|
||||||
.form-control:focus, .form-select:focus {
|
|
||||||
border-color: #1D4ED8;
|
|
||||||
box-shadow: 0 0 0 0.25rem rgba(29, 78, 216, 0.25);
|
|
||||||
}
|
|
||||||
|
|
||||||
.table {
|
|
||||||
vertical-align: middle;
|
|
||||||
}
|
|
||||||
|
|
||||||
.duration-input-group {
|
|
||||||
direction: ltr;
|
|
||||||
}
|
|
||||||
|
|
||||||
.toast-container {
|
|
||||||
z-index: 1080;
|
|
||||||
}
|
|
||||||
@ -1,130 +0,0 @@
|
|||||||
document.addEventListener('DOMContentLoaded', function () {
|
|
||||||
const lineupId = new URLSearchParams(window.location.search).get('id');
|
|
||||||
const songSearchInput = document.getElementById('song-search-input');
|
|
||||||
const searchResultsContainer = document.getElementById('search-results');
|
|
||||||
const lineupSongsContainer = document.getElementById('lineup-song-list');
|
|
||||||
|
|
||||||
// Function to fetch and display songs already in the lineup
|
|
||||||
function fetchLineupSongs() {
|
|
||||||
if (!lineupId) return;
|
|
||||||
fetch(`/api/lineups_api.php?lineup_id=${lineupId}`)
|
|
||||||
.then(response => response.json())
|
|
||||||
.then(data => {
|
|
||||||
renderLineupSongs(data);
|
|
||||||
})
|
|
||||||
.catch(error => console.error('Error fetching lineup songs:', error));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Function to render the list of songs in the lineup
|
|
||||||
function renderLineupSongs(songs) {
|
|
||||||
lineupSongsContainer.innerHTML = '';
|
|
||||||
if (songs.length === 0) {
|
|
||||||
lineupSongsContainer.innerHTML = '<p>אין עדיין שירים בליינאפ זה.</p>';
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const list = document.createElement('ul');
|
|
||||||
list.className = 'list-group';
|
|
||||||
songs.forEach(song => {
|
|
||||||
const listItem = document.createElement('li');
|
|
||||||
listItem.className = 'list-group-item d-flex justify-content-between align-items-center';
|
|
||||||
listItem.textContent = `${song.artist || 'Unknown Artist'} - ${song.name}`;
|
|
||||||
|
|
||||||
const removeBtn = document.createElement('button');
|
|
||||||
removeBtn.className = 'btn btn-danger btn-sm';
|
|
||||||
removeBtn.textContent = 'הסר';
|
|
||||||
removeBtn.onclick = () => removeSongFromLineup(song.id); // Use song.id from the songs table
|
|
||||||
|
|
||||||
listItem.appendChild(removeBtn);
|
|
||||||
list.appendChild(listItem);
|
|
||||||
});
|
|
||||||
lineupSongsContainer.appendChild(list);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Function to search for songs
|
|
||||||
function searchSongs(query) {
|
|
||||||
fetch(`/api/search_songs.php?q=${encodeURIComponent(query)}`)
|
|
||||||
.then(response => response.json())
|
|
||||||
.then(data => {
|
|
||||||
renderSearchResults(data);
|
|
||||||
})
|
|
||||||
.catch(error => console.error('Error searching songs:', error));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Function to render search results
|
|
||||||
function renderSearchResults(songs) {
|
|
||||||
searchResultsContainer.innerHTML = '';
|
|
||||||
if (songs.length === 0) {
|
|
||||||
searchResultsContainer.innerHTML = '<p>לא נמצאו שירים.</p>';
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const list = document.createElement('ul');
|
|
||||||
list.className = 'list-group';
|
|
||||||
songs.forEach(song => {
|
|
||||||
const listItem = document.createElement('li');
|
|
||||||
listItem.className = 'list-group-item d-flex justify-content-between align-items-center';
|
|
||||||
listItem.textContent = `${song.artist || 'Unknown Artist'} - ${song.name}`;
|
|
||||||
|
|
||||||
const addBtn = document.createElement('button');
|
|
||||||
addBtn.className = 'btn btn-primary btn-sm';
|
|
||||||
addBtn.textContent = 'הוסף';
|
|
||||||
addBtn.onclick = () => addSongToLineup(song.id);
|
|
||||||
|
|
||||||
listItem.appendChild(addBtn);
|
|
||||||
list.appendChild(listItem);
|
|
||||||
});
|
|
||||||
searchResultsContainer.appendChild(list);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Function to add a song to the lineup
|
|
||||||
function addSongToLineup(songId) {
|
|
||||||
fetch('/api/add_song_to_lineup.php', {
|
|
||||||
method: 'POST',
|
|
||||||
headers: { 'Content-Type': 'application/json' },
|
|
||||||
body: JSON.stringify({ lineup_id: lineupId, song_id: songId })
|
|
||||||
})
|
|
||||||
.then(response => response.json().then(data => ({ status: response.status, body: data })))
|
|
||||||
.then(({ status, body }) => {
|
|
||||||
if (status === 200 && body.success) {
|
|
||||||
fetchLineupSongs(); // Refresh the lineup list
|
|
||||||
const currentQuery = searchInput.value.trim();
|
|
||||||
searchSongs(currentQuery); // Refresh search results to remove the added song
|
|
||||||
} else {
|
|
||||||
// Use the specific message from the server, or a default one
|
|
||||||
alert(body.message || 'לא ניתן היה להוסיף את השיר.');
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch(error => console.error('Error adding song:', error));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Function to remove a song from the lineup
|
|
||||||
function removeSongFromLineup(songId) {
|
|
||||||
fetch('/api/remove_song_from_lineup.php', {
|
|
||||||
method: 'POST',
|
|
||||||
headers: { 'Content-Type': 'application/json' },
|
|
||||||
body: JSON.stringify({ lineup_id: lineupId, song_id: songId })
|
|
||||||
})
|
|
||||||
.then(response => response.json())
|
|
||||||
.then(data => {
|
|
||||||
if (data.success) {
|
|
||||||
fetchLineupSongs(); // Refresh the lineup list
|
|
||||||
const currentQuery = searchInput.value.trim();
|
|
||||||
searchSongs(currentQuery); // Refresh search results to show the removed song
|
|
||||||
} else {
|
|
||||||
alert('Failed to remove song: ' + (data.error || 'Unknown error'));
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch(error => console.error('Error removing song:', error));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initial fetch of lineup songs and all songs for searching
|
|
||||||
if (lineupId) {
|
|
||||||
fetchLineupSongs();
|
|
||||||
searchSongs(''); // Load all songs initially
|
|
||||||
}
|
|
||||||
|
|
||||||
// Event Listener for the search input
|
|
||||||
searchInput.addEventListener('input', () => {
|
|
||||||
const query = searchInput.value.trim();
|
|
||||||
searchSongs(query);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@ -1,41 +0,0 @@
|
|||||||
document.addEventListener('DOMContentLoaded', function () {
|
|
||||||
// Handle Create Lineup form submission
|
|
||||||
const createLineupForm = document.getElementById('createLineupForm');
|
|
||||||
if (createLineupForm) {
|
|
||||||
createLineupForm.addEventListener('submit', function(event) {
|
|
||||||
event.preventDefault();
|
|
||||||
const lineupName = document.getElementById('lineupName').value;
|
|
||||||
// The submit button is outside the form, so we find it in the modal footer
|
|
||||||
const createButton = this.closest('.modal-content').querySelector('.modal-footer button[type="submit"]');
|
|
||||||
const originalButtonText = createButton.innerHTML;
|
|
||||||
|
|
||||||
createButton.disabled = true;
|
|
||||||
createButton.innerHTML = '<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span> יוצר...';
|
|
||||||
|
|
||||||
fetch('api/lineups_api.php', {
|
|
||||||
method: 'POST',
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
},
|
|
||||||
body: JSON.stringify({ action: 'create', name: lineupName }),
|
|
||||||
})
|
|
||||||
.then(response => response.json())
|
|
||||||
.then(data => {
|
|
||||||
if (data.success) {
|
|
||||||
window.location.reload(); // Reload to see the new lineup
|
|
||||||
} else {
|
|
||||||
alert('שגיאה ביצירת הליינאפ: ' + data.error);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch(error => {
|
|
||||||
console.error('Error:', error);
|
|
||||||
alert('אירעה שגיאה בלתי צפויה.');
|
|
||||||
})
|
|
||||||
.finally(() => {
|
|
||||||
// This might not be reached if the page reloads, but it's good practice
|
|
||||||
createButton.disabled = false;
|
|
||||||
createButton.innerHTML = originalButtonText;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
@ -1,178 +0,0 @@
|
|||||||
document.addEventListener('DOMContentLoaded', function () {
|
|
||||||
// Live search for songs page
|
|
||||||
const searchInput = document.getElementById('searchInput');
|
|
||||||
const clearSearchBtn = document.getElementById('clearSearchBtn');
|
|
||||||
const tableBody = document.getElementById('songsTableBody');
|
|
||||||
const originalNoSongsRow = document.getElementById('noSongsRow')?.cloneNode(true);
|
|
||||||
|
|
||||||
if (searchInput && tableBody) {
|
|
||||||
// Function to format duration from seconds to MM:SS
|
|
||||||
const formatDuration = (seconds) => {
|
|
||||||
if (seconds === null || seconds < 0) return '00:00';
|
|
||||||
const mins = Math.floor(seconds / 60);
|
|
||||||
const secs = seconds % 60;
|
|
||||||
return `${String(mins).padStart(2, '0')}:${String(secs).padStart(2, '0')}`;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Function to render table rows
|
|
||||||
const renderTable = (songs, searchTerm) => {
|
|
||||||
tableBody.innerHTML = ''; // Clear existing rows
|
|
||||||
|
|
||||||
if (songs.length === 0) {
|
|
||||||
const noResultsRow = document.createElement('tr');
|
|
||||||
noResultsRow.innerHTML = `<td colspan="9" class="text-center text-muted py-4">לא נמצאו שירים התואמים את החיפוש "${searchTerm}".</td>`;
|
|
||||||
if (searchTerm === '') {
|
|
||||||
if(originalNoSongsRow){
|
|
||||||
tableBody.appendChild(originalNoSongsRow);
|
|
||||||
} else {
|
|
||||||
noResultsRow.innerHTML = `<td colspan="9" class="text-center text-muted py-4">עדיין אין שירים במאגר. <a href="#" onclick="document.getElementById('addSongBtn').click()">הוסף את השיר הראשון שלך!</a></td>`;
|
|
||||||
tableBody.appendChild(noResultsRow);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
tableBody.appendChild(noResultsRow);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
songs.forEach((song, index) => {
|
|
||||||
const row = document.createElement('tr');
|
|
||||||
const tagsHtml = song.tags.split(',').map(tag => {
|
|
||||||
const trimmedTag = tag.trim();
|
|
||||||
return trimmedTag ? `<span class="badge bg-secondary bg-opacity-25 text-dark-emphasis">${escapeHTML(trimmedTag)}</span>` : '';
|
|
||||||
}).join(' ');
|
|
||||||
|
|
||||||
row.innerHTML = `
|
|
||||||
<td>${index + 1}</td>
|
|
||||||
<td>${escapeHTML(song.artist)}</td>
|
|
||||||
<td class="fw-bold">${escapeHTML(song.name)}</td>
|
|
||||||
<td>${escapeHTML(song.bpm)}</td>
|
|
||||||
<td>${escapeHTML(song.song_key)}</td>
|
|
||||||
<td>${formatDuration(song.duration_seconds)}</td>
|
|
||||||
<td>${tagsHtml}</td>
|
|
||||||
<td>${escapeHTML(song.notes)}</td>
|
|
||||||
<td>
|
|
||||||
<button class="btn btn-sm btn-outline-primary edit-btn" data-song='${escapeHTML(JSON.stringify(song))}' data-bs-toggle="modal" data-bs-target="#songModal">
|
|
||||||
<i class="bi bi-pencil"></i>
|
|
||||||
</button>
|
|
||||||
<form action="songs.php" method="POST" class="d-inline" onsubmit="return confirm('האם אתה בטוח שברצונך למחוק את השיר?');">
|
|
||||||
<input type="hidden" name="action" value="delete">
|
|
||||||
<input type="hidden" name="id" value="${song.id}">
|
|
||||||
<button type="submit" class="btn btn-sm btn-outline-danger">
|
|
||||||
<i class="bi bi-trash"></i>
|
|
||||||
</button>
|
|
||||||
</form>
|
|
||||||
</td>
|
|
||||||
`;
|
|
||||||
tableBody.appendChild(row);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Debounce function to limit API calls
|
|
||||||
let debounceTimeout;
|
|
||||||
const handleSearch = () => {
|
|
||||||
const searchTerm = searchInput.value.trim();
|
|
||||||
clearTimeout(debounceTimeout);
|
|
||||||
|
|
||||||
clearSearchBtn.style.display = searchTerm ? 'block' : 'none';
|
|
||||||
|
|
||||||
debounceTimeout = setTimeout(() => {
|
|
||||||
fetch(`api/search_songs.php?search=${encodeURIComponent(searchTerm)}`)
|
|
||||||
.then(response => response.json())
|
|
||||||
.then(songs => {
|
|
||||||
renderTable(songs, searchTerm);
|
|
||||||
})
|
|
||||||
.catch(error => console.error('Error fetching search results:', error));
|
|
||||||
}, 300); // 300ms delay
|
|
||||||
};
|
|
||||||
|
|
||||||
searchInput.addEventListener('input', handleSearch);
|
|
||||||
|
|
||||||
clearSearchBtn.addEventListener('click', () => {
|
|
||||||
searchInput.value = '';
|
|
||||||
handleSearch();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function escapeHTML(str) {
|
|
||||||
if (str === null || str === undefined) return '';
|
|
||||||
return String(str).replace(/[&<>'"/]/g, function (s) {
|
|
||||||
return {
|
|
||||||
'&': '&',
|
|
||||||
'<': '<',
|
|
||||||
'>': '>',
|
|
||||||
'"': '"',
|
|
||||||
"'": ''',
|
|
||||||
'/': '/'
|
|
||||||
}[s];
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const songModalEl = document.getElementById('songModal');
|
|
||||||
if (songModalEl) {
|
|
||||||
const songModal = new bootstrap.Modal(songModalEl);
|
|
||||||
const modalTitle = songModalEl.querySelector('.modal-title');
|
|
||||||
const songForm = document.getElementById('songForm');
|
|
||||||
const actionInput = document.getElementById('action');
|
|
||||||
const songIdInput = document.getElementById('song_id');
|
|
||||||
|
|
||||||
// Function to reset the modal to its "Add Song" state
|
|
||||||
const resetModal = () => {
|
|
||||||
songForm.reset();
|
|
||||||
actionInput.value = 'create';
|
|
||||||
songIdInput.value = '';
|
|
||||||
modalTitle.textContent = 'הוספת שיר חדש';
|
|
||||||
};
|
|
||||||
|
|
||||||
// Use event delegation for edit buttons since they are dynamically loaded
|
|
||||||
document.body.addEventListener('click', function(event) {
|
|
||||||
const editBtn = event.target.closest('.edit-btn');
|
|
||||||
if (editBtn) {
|
|
||||||
const song = JSON.parse(editBtn.dataset.song);
|
|
||||||
|
|
||||||
resetModal(); // Start with a clean slate
|
|
||||||
|
|
||||||
actionInput.value = 'update';
|
|
||||||
modalTitle.textContent = 'עריכת שיר';
|
|
||||||
songIdInput.value = song.id;
|
|
||||||
|
|
||||||
document.getElementById('name').value = song.name;
|
|
||||||
document.getElementById('artist').value = song.artist;
|
|
||||||
document.getElementById('bpm').value = song.bpm;
|
|
||||||
|
|
||||||
if (song.song_key && song.song_key.trim() !== '') {
|
|
||||||
const keyParts = song.song_key.split(' ');
|
|
||||||
document.getElementById('key_note').value = keyParts[0];
|
|
||||||
document.getElementById('key_scale').value = keyParts[1] || 'Major';
|
|
||||||
} else {
|
|
||||||
document.getElementById('key_note').value = '';
|
|
||||||
document.getElementById('key_scale').value = 'Major';
|
|
||||||
}
|
|
||||||
|
|
||||||
document.getElementById('notes').value = song.notes;
|
|
||||||
document.getElementById('tags').value = song.tags;
|
|
||||||
|
|
||||||
if (song.duration_seconds) {
|
|
||||||
const minutes = Math.floor(song.duration_seconds / 60);
|
|
||||||
const seconds = song.duration_seconds % 60;
|
|
||||||
document.getElementById('duration_minutes').value = minutes;
|
|
||||||
document.getElementById('duration_seconds').value = seconds;
|
|
||||||
}
|
|
||||||
|
|
||||||
songModal.show();
|
|
||||||
}
|
|
||||||
|
|
||||||
const addSongLink = event.target.closest('#addSongBtnLink');
|
|
||||||
if(addSongLink){
|
|
||||||
event.preventDefault();
|
|
||||||
resetModal();
|
|
||||||
songModal.show();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Reset the modal form when it's opened via the main "Add Song" button
|
|
||||||
songModalEl.addEventListener('show.bs.modal', function (event) {
|
|
||||||
if (event.relatedTarget && !event.relatedTarget.classList.contains('edit-btn')) {
|
|
||||||
resetModal();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 22 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 13 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 23 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 8.3 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 24 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 38 KiB |
@ -1,26 +0,0 @@
|
|||||||
-- Initial Schema for Lineup Application
|
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS `lineups` (
|
|
||||||
`id` INT AUTO_INCREMENT PRIMARY KEY,
|
|
||||||
`name` VARCHAR(255) NOT NULL,
|
|
||||||
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS `songs` (
|
|
||||||
`id` INT AUTO_INCREMENT PRIMARY KEY,
|
|
||||||
`title` VARCHAR(255) NOT NULL,
|
|
||||||
`artist` VARCHAR(255) NOT NULL,
|
|
||||||
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS `lineup_songs` (
|
|
||||||
`id` INT AUTO_INCREMENT PRIMARY KEY,
|
|
||||||
`lineup_id` INT NOT NULL,
|
|
||||||
`song_id` INT NOT NULL,
|
|
||||||
`display_order` INT NOT NULL DEFAULT 0,
|
|
||||||
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
FOREIGN KEY (`lineup_id`) REFERENCES `lineups`(`id`) ON DELETE CASCADE,
|
|
||||||
FOREIGN KEY (`song_id`) REFERENCES `songs`(`id`) ON DELETE CASCADE,
|
|
||||||
UNIQUE KEY `lineup_song_unique` (`lineup_id`, `song_id`)
|
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
|
||||||
|
|
||||||
@ -1,7 +0,0 @@
|
|||||||
-- Add indexes to lineup_songs for faster lookups
|
|
||||||
ALTER TABLE `lineup_songs` ADD INDEX `idx_lineup_id` (`lineup_id`);
|
|
||||||
ALTER TABLE `lineup_songs` ADD INDEX `idx_song_id` (`song_id`);
|
|
||||||
|
|
||||||
-- Add a unique constraint to prevent adding the same song to a lineup twice
|
|
||||||
-- This also creates an index
|
|
||||||
ALTER TABLE `lineup_songs` ADD UNIQUE `unique_lineup_song` (`lineup_id`, `song_id`);
|
|
||||||
@ -1,2 +0,0 @@
|
|||||||
-- Add display_order column to lineup_songs table
|
|
||||||
ALTER TABLE `lineup_songs` ADD COLUMN `display_order` INT NOT NULL DEFAULT 0 AFTER `song_id`;
|
|
||||||
@ -1,2 +0,0 @@
|
|||||||
-- Add index for display_order to speed up sorting
|
|
||||||
ALTER TABLE `lineup_songs` ADD INDEX `idx_display_order` (`display_order`);
|
|
||||||
@ -1 +0,0 @@
|
|||||||
ALTER TABLE `songs` ADD `artist` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL AFTER `name`;
|
|
||||||
@ -1,2 +0,0 @@
|
|||||||
-- Add index for song_order to speed up sorting
|
|
||||||
ALTER TABLE `lineup_songs` ADD INDEX `idx_song_order` (`song_order`);
|
|
||||||
@ -1,44 +0,0 @@
|
|||||||
<?php
|
|
||||||
ini_set('display_errors', 1);
|
|
||||||
error_reporting(E_ALL);
|
|
||||||
|
|
||||||
echo '<h1>מצב מאגר השירים</h1>';
|
|
||||||
echo '<p>בעמוד זה מוצגת רשימת השירים כפי שהיא שמורה כרגע במסד הנתונים.</p>';
|
|
||||||
|
|
||||||
try {
|
|
||||||
require_once 'db/config.php';
|
|
||||||
$pdo = db();
|
|
||||||
|
|
||||||
$stmt = $pdo->query("SELECT * FROM songs ORDER BY id ASC");
|
|
||||||
$songs = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
|
||||||
|
|
||||||
if (empty($songs)) {
|
|
||||||
echo '<div style="border: 1px solid #ccc; padding: 15px; background-color: #f0f0f0;"><strong>המאגר ריק.</strong> לא נמצאו שירים.</div>';
|
|
||||||
} else {
|
|
||||||
echo '<table border="1" cellpadding="5" cellspacing="0" style="width: 100%; border-collapse: collapse;">';
|
|
||||||
echo '<thead style="background-color: #f2f2f2;"><tr><th>ID</th><th>Name</th><th>Artist</th><th>BPM</th><th>Key</th><th>Duration</th><th>Tags</th><th>Notes</th></tr></thead>';
|
|
||||||
echo '<tbody>';
|
|
||||||
foreach ($songs as $song) {
|
|
||||||
echo '<tr>';
|
|
||||||
echo '<td>' . htmlspecialchars($song['id']) . '</td>';
|
|
||||||
echo '<td>' . htmlspecialchars($song['name']) . '</td>';
|
|
||||||
echo '<td>' . htmlspecialchars($song['artist'] ?? 'N/A') . '</td>';
|
|
||||||
echo '<td>' . htmlspecialchars($song['bpm'] ?? 'N/A') . '</td>';
|
|
||||||
echo '<td>' . htmlspecialchars($song['song_key'] ?? 'N/A') . '</td>';
|
|
||||||
echo '<td>' . htmlspecialchars($song['duration_seconds'] ?? 'N/A') . '</td>';
|
|
||||||
echo '<td>' . htmlspecialchars($song['tags'] ?? 'N/A') . '</td>';
|
|
||||||
echo '<td>' . htmlspecialchars($song['notes'] ?? 'N/A') . '</td>';
|
|
||||||
echo '</tr>';
|
|
||||||
}
|
|
||||||
echo '</tbody></table>';
|
|
||||||
echo '<p style="margin-top: 15px;"><strong>סה"כ שירים: ' . count($songs) . '</strong></p>';
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (Exception $e) {
|
|
||||||
echo '<div style="border: 1px solid red; padding: 15px; background-color: #ffebeb; color: red;">';
|
|
||||||
echo '<strong>שגיאה חמורה!</strong><br>';
|
|
||||||
echo 'לא ניתן היה להתחבר למסד הנתונים או לשלוף את המידע.<br>';
|
|
||||||
echo 'פרטי השגיאה: ' . $e->getMessage();
|
|
||||||
echo '</div>';
|
|
||||||
}
|
|
||||||
?>
|
|
||||||
@ -1,9 +0,0 @@
|
|||||||
</main>
|
|
||||||
<footer class="text-center py-4 text-muted border-top mt-5">
|
|
||||||
<p>© <?php echo date('Y'); ?> Ari Stage. Built with Flatlogic.</p>
|
|
||||||
</footer>
|
|
||||||
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
|
|
||||||
<script src="assets/js/main.js?v=<?php echo time(); ?>"></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@ -1,45 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="he" dir="rtl">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
||||||
<title>Ari Stage - ניהול מופעים</title>
|
|
||||||
<meta name="description" content="מערכת ליינאפ וניהול מופעים לזמרים ומפיקים. נבנה עם Flatlogic.">
|
|
||||||
<meta name="keywords" content="ניהול הופעות, ליינאפ, רשימת שירים, ניהול שירים, מוזיקה, זמרים, מפיקים, Ari Stage, Flatlogic">
|
|
||||||
<meta property="og:title" content="Ari Stage">
|
|
||||||
<meta property="og:description" content="מערכת ליינאפ וניהול מופעים לזמרים ומפיקים.">
|
|
||||||
<meta property="og:image" content="">
|
|
||||||
<meta name="twitter:card" content="summary_large_image">
|
|
||||||
<meta name="twitter:image" content="">
|
|
||||||
|
|
||||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.rtl.min.css" rel="stylesheet">
|
|
||||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css">
|
|
||||||
<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=Heebo:wght@400;500;700&display=swap" rel="stylesheet">
|
|
||||||
<link rel="stylesheet" href="assets/css/custom.css?v=<?php echo time(); ?>">
|
|
||||||
</head>
|
|
||||||
<body class="bg-light">
|
|
||||||
<nav class="navbar navbar-expand-lg navbar-dark" style="background-color: #1D4ED8;">
|
|
||||||
<div class="container-fluid">
|
|
||||||
<a class="navbar-brand fw-bold" href="index.php">Ari Stage</a>
|
|
||||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
|
|
||||||
<span class="navbar-toggler-icon"></span>
|
|
||||||
</button>
|
|
||||||
<div class="collapse navbar-collapse" id="navbarNav">
|
|
||||||
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
|
|
||||||
<?php $pageName = basename($_SERVER['PHP_SELF'], '.php'); ?>
|
|
||||||
<li class="nav-item">
|
|
||||||
<a class="nav-link <?php echo ($pageName == 'index') ? 'active' : ''; ?>" <?php echo ($pageName == 'index') ? 'aria-current="page"' : ''; ?> href="index.php">בית</a>
|
|
||||||
</li>
|
|
||||||
<li class="nav-item">
|
|
||||||
<a class="nav-link <?php echo ($pageName == 'songs') ? 'active' : ''; ?>" <?php echo ($pageName == 'songs') ? 'aria-current="page"' : ''; ?> href="songs.php">מאגר שירים</a>
|
|
||||||
</li>
|
|
||||||
<li class="nav-item">
|
|
||||||
<a class="nav-link <?php echo ($pageName == 'lineups') ? 'active' : ''; ?>" <?php echo ($pageName == 'lineups') ? 'aria-current="page"' : ''; ?> href="lineups.php">ליינאפים</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</nav>
|
|
||||||
<main class="container my-5">
|
|
||||||
161
index.php
161
index.php
@ -1,17 +1,150 @@
|
|||||||
<?php
|
<?php
|
||||||
$pageName = 'index';
|
declare(strict_types=1);
|
||||||
include 'includes/header.php';
|
@ini_set('display_errors', '1');
|
||||||
|
@error_reporting(E_ALL);
|
||||||
|
@date_default_timezone_set('UTC');
|
||||||
|
|
||||||
|
$phpVersion = PHP_VERSION;
|
||||||
|
$now = date('Y-m-d H:i:s');
|
||||||
?>
|
?>
|
||||||
|
<!doctype html>
|
||||||
<div class="text-center">
|
<html lang="en">
|
||||||
<h1 class="display-4 fw-bold">ברוכים הבאים ל-Ari Stage</h1>
|
<head>
|
||||||
<p class="lead col-lg-6 mx-auto text-muted">
|
<meta charset="utf-8" />
|
||||||
הכלי המושלם לזמרים ומפיקים לניהול מאגר השירים, יצירת ליינאפים דינמיים, וארגון הופעות.
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
</p>
|
<title>New Style</title>
|
||||||
<div class="d-grid gap-2 d-sm-flex justify-content-sm-center mt-5">
|
<?php
|
||||||
<a href="songs.php" class="btn btn-primary btn-lg px-4 gap-3">למאגר השירים שלי</a>
|
// Read project preview data from environment
|
||||||
<a href="#" class="btn btn-outline-secondary btn-lg px-4 disabled">צור ליינאפ חדש</a>
|
$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>
|
</div>
|
||||||
</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>
|
||||||
<?php include 'includes/footer.php'; ?>
|
<p>Runtime: PHP <code><?= htmlspecialchars($phpVersion) ?></code> — UTC <code><?= htmlspecialchars($now) ?></code></p>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
<footer>
|
||||||
|
Page updated: <?= htmlspecialchars($now) ?> (UTC)
|
||||||
|
</footer>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|||||||
@ -1,95 +0,0 @@
|
|||||||
<?php
|
|
||||||
$pageName = 'פרטי ליינאפ';
|
|
||||||
require_once 'includes/header.php';
|
|
||||||
require_once 'db/config.php';
|
|
||||||
|
|
||||||
// Check if lineup ID is provided
|
|
||||||
if (!isset($_GET['id']) || !is_numeric($_GET['id'])) {
|
|
||||||
echo '<div class="container mt-4"><div class="alert alert-danger">מזהה ליינאפ לא תקין.</div></div>';
|
|
||||||
require_once 'includes/footer.php';
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
$lineup_id = intval($_GET['id']);
|
|
||||||
$db = db();
|
|
||||||
|
|
||||||
// Fetch lineup details
|
|
||||||
$stmt = $db->prepare("SELECT * FROM lineups WHERE id = ?");
|
|
||||||
$stmt->execute([$lineup_id]);
|
|
||||||
$lineup = $stmt->fetch(PDO::FETCH_ASSOC);
|
|
||||||
|
|
||||||
if (!$lineup) {
|
|
||||||
echo '<div class="container mt-4"><div class="alert alert-danger">הליינאפ לא נמצא.</div></div>';
|
|
||||||
require_once 'includes/footer.php';
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fetch songs for this lineup (to be implemented)
|
|
||||||
$songs_stmt = $db->prepare("
|
|
||||||
SELECT s.* FROM songs s
|
|
||||||
JOIN lineup_songs ls ON s.id = ls.song_id
|
|
||||||
WHERE ls.lineup_id = ?
|
|
||||||
ORDER BY ls.song_order ASC
|
|
||||||
");
|
|
||||||
$songs_stmt->execute([$lineup_id]);
|
|
||||||
$songs = $songs_stmt->fetchAll(PDO::FETCH_ASSOC);
|
|
||||||
|
|
||||||
?>
|
|
||||||
|
|
||||||
<div class="container mt-4">
|
|
||||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
|
||||||
<h1><?php echo htmlspecialchars($lineup['name']); ?></h1>
|
|
||||||
<a href="lineups.php" class="btn btn-outline-secondary">חזרה לרשימת הליינאפים</a>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
<div class="row">
|
|
||||||
<!-- Add Songs -->
|
|
||||||
<div class="col-md-6">
|
|
||||||
<h3>הוספת שירים</h3>
|
|
||||||
<div class="card">
|
|
||||||
<div class="card-body">
|
|
||||||
<div class="mb-3">
|
|
||||||
<label for="search-song-input" class="form-label">חפש שיר</label>
|
|
||||||
<input type="text" class="form-control" id="song-search-input" placeholder="הקלד שם שיר או אמן...">
|
|
||||||
</div>
|
|
||||||
<div id="search-results" style="max-height: 300px; overflow-y: auto;">
|
|
||||||
<!-- Search results will appear here -->
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Song List -->
|
|
||||||
<div class="col-md-6">
|
|
||||||
<h3>שירים בליינאפ</h3>
|
|
||||||
<div id="lineup-songs-container">
|
|
||||||
<ul class="list-group" id="lineup-song-list">
|
|
||||||
<?php if (empty($songs)): ?>
|
|
||||||
<li id="empty-lineup-message" class="list-group-item text-center text-muted">
|
|
||||||
אין עדיין שירים בליינאפ זה.
|
|
||||||
</li>
|
|
||||||
<?php else: ?>
|
|
||||||
<?php foreach ($songs as $song): ?>
|
|
||||||
<li class="list-group-item d-flex justify-content-between align-items-center" data-song-id="<?php echo $song['id']; ?>">
|
|
||||||
<span>
|
|
||||||
<i class="fas fa-grip-vertical me-2"></i>
|
|
||||||
<strong><?php echo htmlspecialchars($song['artist']); ?></strong> - <?php echo htmlspecialchars($song['name']); ?>
|
|
||||||
</span>
|
|
||||||
<button class="btn btn-sm btn-outline-danger remove-song-btn" data-song-id="<?php echo $song['id']; ?>" data-lineup-id="<?php echo $lineup_id; ?>">הסר</button>
|
|
||||||
</li>
|
|
||||||
<?php endforeach; ?>
|
|
||||||
<?php endif; ?>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<input type="hidden" id="lineup-id" value="<?php echo $lineup_id; ?>">
|
|
||||||
|
|
||||||
<script src="assets/js/lineup_details_page.js?v=<?php echo time(); ?>"></script>
|
|
||||||
|
|
||||||
<?php
|
|
||||||
require_once 'includes/footer.php';
|
|
||||||
?>
|
|
||||||
72
lineups.php
72
lineups.php
@ -1,72 +0,0 @@
|
|||||||
<?php
|
|
||||||
$pageTitle = 'הליינאפים שלי';
|
|
||||||
$pageName = 'lineups';
|
|
||||||
require_once __DIR__ . '/db/config.php';
|
|
||||||
require_once __DIR__ . '/includes/header.php';
|
|
||||||
?>
|
|
||||||
|
|
||||||
<div class="container mt-4">
|
|
||||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
|
||||||
<h1>הליינאפים שלי</h1>
|
|
||||||
<button class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#createLineupModal">צור ליינאפ חדש</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<?php
|
|
||||||
try {
|
|
||||||
$pdo = db();
|
|
||||||
$stmt = $pdo->query("SELECT * FROM lineups ORDER BY created_at DESC");
|
|
||||||
$lineups = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
|
||||||
} catch (PDOException $e) {
|
|
||||||
// Handle DB error
|
|
||||||
$lineups = [];
|
|
||||||
echo "<div class='alert alert-danger'>שגיאה בטעינת הליינאפים: " . htmlspecialchars($e->getMessage()) . "</div>";
|
|
||||||
}
|
|
||||||
?>
|
|
||||||
|
|
||||||
<?php if (empty($lineups)): ?>
|
|
||||||
<div class="text-center text-muted py-5">
|
|
||||||
<i class="bi bi-music-note-list" style="font-size: 3rem;"></i>
|
|
||||||
<p class="mt-3">עדיין לא יצרת ליינאפים.</p>
|
|
||||||
<p>לחץ על "צור ליינאפ חדש" כדי להתחיל.</p>
|
|
||||||
</div>
|
|
||||||
<?php else: ?>
|
|
||||||
<div class="list-group">
|
|
||||||
<?php foreach ($lineups as $lineup): ?>
|
|
||||||
<a href="lineup_details.php?id=<?php echo $lineup['id']; ?>" class="list-group-item list-group-item-action d-flex justify-content-between align-items-center">
|
|
||||||
<div>
|
|
||||||
<h5 class="mb-1"><?php echo htmlspecialchars($lineup['name']); ?></h5>
|
|
||||||
<small class="text-muted">נוצר ב: <?php echo date('d/m/Y', strtotime($lineup['created_at'])); ?></small>
|
|
||||||
</div>
|
|
||||||
<i class="bi bi-chevron-left"></i>
|
|
||||||
</a>
|
|
||||||
<?php endforeach; ?>
|
|
||||||
</div>
|
|
||||||
<?php endif; ?>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Create Lineup Modal -->
|
|
||||||
<div class="modal fade" id="createLineupModal" tabindex="-1" aria-labelledby="createLineupModalLabel" aria-hidden="true">
|
|
||||||
<div class="modal-dialog">
|
|
||||||
<div class="modal-content">
|
|
||||||
<div class="modal-header">
|
|
||||||
<h5 class="modal-title" id="createLineupModalLabel">יצירת ליינאפ חדש</h5>
|
|
||||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
|
||||||
</div>
|
|
||||||
<div class="modal-body">
|
|
||||||
<form id="createLineupForm">
|
|
||||||
<div class="mb-3">
|
|
||||||
<label for="lineupName" class="form-label">שם הליינאפ</label>
|
|
||||||
<input type="text" class="form-control" id="lineupName" name="lineupName" required>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
<div class="modal-footer">
|
|
||||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">סגור</button>
|
|
||||||
<button type="submit" form="createLineupForm" class="btn btn-primary">צור</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<?php require_once __DIR__ . '/includes/footer.php'; ?>
|
|
||||||
77
migrate.php
77
migrate.php
@ -1,77 +0,0 @@
|
|||||||
<?php
|
|
||||||
header('Content-Type: text/plain; charset=utf-8');
|
|
||||||
|
|
||||||
require_once 'db/config.php';
|
|
||||||
|
|
||||||
echo "Starting database migration...\n\n";
|
|
||||||
|
|
||||||
try {
|
|
||||||
$pdo = db();
|
|
||||||
|
|
||||||
// 1. Create migrations tracking table if it doesn't exist
|
|
||||||
$pdo->exec("CREATE TABLE IF NOT EXISTS `migrations` (
|
|
||||||
`id` INT AUTO_INCREMENT PRIMARY KEY,
|
|
||||||
`migration_file` VARCHAR(255) NOT NULL,
|
|
||||||
`applied_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
UNIQUE KEY `migration_file_unique` (`migration_file`)
|
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;");
|
|
||||||
echo "1. 'migrations' table is ready.\n";
|
|
||||||
|
|
||||||
// 2. Get all migrations that have already been run
|
|
||||||
$stmt = $pdo->query("SELECT `migration_file` FROM `migrations`");
|
|
||||||
$applied_migrations = $stmt->fetchAll(PDO::FETCH_COLUMN);
|
|
||||||
echo "2. Found " . count($applied_migrations) . " applied migrations.\n";
|
|
||||||
|
|
||||||
// 3. Find all migration files on disk
|
|
||||||
$migration_files = glob('db/migrations/*.sql');
|
|
||||||
sort($migration_files);
|
|
||||||
echo "3. Found " . count($migration_files) . " migration files on disk.\n";
|
|
||||||
|
|
||||||
|
|
||||||
// 4. Apply pending migrations
|
|
||||||
$migrations_applied_count = 0;
|
|
||||||
foreach ($migration_files as $file) {
|
|
||||||
$basename = basename($file);
|
|
||||||
if (in_array($basename, $applied_migrations)) {
|
|
||||||
echo "- Skipping already applied migration: {$basename}\n";
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
echo "+ Applying new migration: {$basename}...\n";
|
|
||||||
$sql = file_get_contents($file);
|
|
||||||
if (empty(trim($sql))) {
|
|
||||||
echo " ...file is empty, skipping.\n";
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
$pdo->beginTransaction();
|
|
||||||
|
|
||||||
$pdo->exec($sql);
|
|
||||||
|
|
||||||
$insert_stmt = $pdo->prepare("INSERT INTO `migrations` (migration_file) VALUES (?)");
|
|
||||||
$insert_stmt->execute([$basename]);
|
|
||||||
|
|
||||||
$pdo->commit();
|
|
||||||
echo " ...SUCCESS!\n";
|
|
||||||
$migrations_applied_count++;
|
|
||||||
|
|
||||||
} catch (Exception $e) {
|
|
||||||
$pdo->rollBack();
|
|
||||||
echo " ...ERROR: " . $e->getMessage() . "\n";
|
|
||||||
echo "\nMigration failed. No changes were made to the database schema.\n";
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($migrations_applied_count > 0) {
|
|
||||||
echo "\nFinished. Applied {" . $migrations_applied_count . "} new migrations successfully.\n";
|
|
||||||
} else {
|
|
||||||
echo "\nFinished. Database is already up to date.\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (Exception $e) {
|
|
||||||
echo "A critical error occurred: " . $e->getMessage() . "\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
?>
|
|
||||||
274
songs.php
274
songs.php
@ -1,274 +0,0 @@
|
|||||||
<?php
|
|
||||||
$pageName = 'songs';
|
|
||||||
session_start();
|
|
||||||
require_once 'db/config.php';
|
|
||||||
|
|
||||||
// --- Logic ---
|
|
||||||
$pdo = db();
|
|
||||||
$notification = null;
|
|
||||||
|
|
||||||
// Handle POST requests for CUD operations
|
|
||||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|
||||||
$action = $_POST['action'] ?? '';
|
|
||||||
|
|
||||||
try {
|
|
||||||
if ($action === 'create' || $action === 'update') {
|
|
||||||
$name = trim($_POST['name'] ?? '');
|
|
||||||
if (empty($name)) {
|
|
||||||
throw new Exception("שם השיר הוא שדה חובה.");
|
|
||||||
}
|
|
||||||
$artist = trim($_POST['artist'] ?? '');
|
|
||||||
|
|
||||||
$bpm = !empty($_POST['bpm']) ? (int)$_POST['bpm'] : null;
|
|
||||||
$key_note = $_POST['key_note'] ?? '';
|
|
||||||
$key_scale = $_POST['key_scale'] ?? '';
|
|
||||||
$song_key = trim($key_note . ' ' . $key_scale);
|
|
||||||
$notes = trim($_POST['notes'] ?? '');
|
|
||||||
$tags = trim($_POST['tags'] ?? '');
|
|
||||||
|
|
||||||
$minutes = !empty($_POST['duration_minutes']) ? (int)$_POST['duration_minutes'] : 0;
|
|
||||||
$seconds = !empty($_POST['duration_seconds']) ? (int)$_POST['duration_seconds'] : 0;
|
|
||||||
$duration_seconds = ($minutes * 60) + $seconds;
|
|
||||||
|
|
||||||
if ($action === 'create') {
|
|
||||||
$stmt = $pdo->prepare("INSERT INTO songs (name, artist, bpm, song_key, duration_seconds, notes, tags) VALUES (?, ?, ?, ?, ?, ?, ?)");
|
|
||||||
$stmt->execute([$name, $artist, $bpm, $song_key, $duration_seconds, $notes, $tags]);
|
|
||||||
$_SESSION['notification'] = ['message' => 'השיר נוצר בהצלחה!', 'type' => 'success'];
|
|
||||||
} else { // update
|
|
||||||
$id = (int)($_POST['id'] ?? 0);
|
|
||||||
if ($id > 0) {
|
|
||||||
$stmt = $pdo->prepare("UPDATE songs SET name=?, artist=?, bpm=?, song_key=?, duration_seconds=?, notes=?, tags=? WHERE id=?");
|
|
||||||
$stmt->execute([$name, $artist, $bpm, $song_key, $duration_seconds, $notes, $tags, $id]);
|
|
||||||
$_SESSION['notification'] = ['message' => 'השיר עודכן בהצלחה!', 'type' => 'success'];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} elseif ($action === 'delete') {
|
|
||||||
$id = (int)($_POST['id'] ?? 0);
|
|
||||||
if ($id > 0) {
|
|
||||||
$stmt = $pdo->prepare("DELETE FROM songs WHERE id=?");
|
|
||||||
$stmt->execute([$id]);
|
|
||||||
$_SESSION['notification'] = ['message' => 'השיר נמחק בהצלחה.', 'type' => 'danger'];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (Exception $e) {
|
|
||||||
$_SESSION['notification'] = ['message' => 'אירעה שגיאה: ' . $e->getMessage(), 'type' => 'danger'];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Redirect to avoid form resubmission
|
|
||||||
header("Location: songs.php");
|
|
||||||
exit();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check for notification from session
|
|
||||||
if (isset($_SESSION['notification'])) {
|
|
||||||
$notification = $_SESSION['notification'];
|
|
||||||
unset($_SESSION['notification']);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Fetch songs with search functionality
|
|
||||||
$search = $_GET['search'] ?? '';
|
|
||||||
$sql = "SELECT * FROM songs";
|
|
||||||
$params = [];
|
|
||||||
if (!empty($search)) {
|
|
||||||
$sql .= " WHERE name LIKE ? OR artist LIKE ? OR bpm LIKE ? OR song_key LIKE ? OR notes LIKE ? OR tags LIKE ?";
|
|
||||||
$searchTerm = "%{$search}%";
|
|
||||||
$params = [$searchTerm, $searchTerm, $searchTerm, $searchTerm, $searchTerm, $searchTerm];
|
|
||||||
}
|
|
||||||
$sql .= " ORDER BY id ASC";
|
|
||||||
$stmt = $pdo->prepare($sql);
|
|
||||||
$stmt->execute($params);
|
|
||||||
$songs = $stmt->fetchAll();
|
|
||||||
|
|
||||||
function format_duration($seconds) {
|
|
||||||
if ($seconds === null || $seconds < 0) return '00:00';
|
|
||||||
$mins = floor($seconds / 60);
|
|
||||||
$secs = $seconds % 60;
|
|
||||||
return sprintf('%02d:%02d', $mins, $secs);
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- Presentation ---
|
|
||||||
include 'includes/header.php';
|
|
||||||
?>
|
|
||||||
|
|
||||||
<!-- Notification Toast -->
|
|
||||||
<?php if ($notification): ?>
|
|
||||||
<div class="toast-container position-fixed bottom-0 start-50 translate-middle-x p-3">
|
|
||||||
<div id="notificationToast" class="toast align-items-center text-bg-<?php echo htmlspecialchars($notification['type']); ?> border-0" role="alert" aria-live="assertive" aria-atomic="true">
|
|
||||||
<div class="d-flex">
|
|
||||||
<div class="toast-body">
|
|
||||||
<?php echo htmlspecialchars($notification['message']); ?>
|
|
||||||
</div>
|
|
||||||
<button type="button" class="btn-close btn-close-white me-2 m-auto" data-bs-dismiss="toast" aria-label="Close"></button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<?php endif; ?>
|
|
||||||
|
|
||||||
|
|
||||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
|
||||||
<h1 class="h2">מאגר השירים</h1>
|
|
||||||
<button id="addSongBtn" class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#songModal"><i class="bi bi-plus-circle me-2"></i>הוסף שיר חדש</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Search Form -->
|
|
||||||
<div class="card mb-4">
|
|
||||||
<div class="card-body p-3">
|
|
||||||
<div class="input-group">
|
|
||||||
<span class="input-group-text"><i class="bi bi-search"></i></span>
|
|
||||||
<input type="search" id="searchInput" class="form-control" placeholder="חיפוש לפי שם, סולם, תג..." value="<?php echo htmlspecialchars($search); ?>">
|
|
||||||
<button id="clearSearchBtn" class="btn btn-outline-secondary" type="button" style="display: none;"><i class="bi bi-x-lg"></i></button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="songsListContainer" class="card">
|
|
||||||
<div class="card-body">
|
|
||||||
<div class="table-responsive">
|
|
||||||
<table class="table table-hover">
|
|
||||||
<thead class="table-light">
|
|
||||||
<tr>
|
|
||||||
<th>#</th>
|
|
||||||
<th>שם האמן</th>
|
|
||||||
<th>שם השיר</th>
|
|
||||||
<th>BPM</th>
|
|
||||||
<th>סולם</th>
|
|
||||||
<th>משך</th>
|
|
||||||
<th>תגים</th>
|
|
||||||
<th>הערות</th>
|
|
||||||
<th>פעולות</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody id="songsTableBody">
|
|
||||||
<?php if (empty($songs)): ?>
|
|
||||||
<tr id="noSongsRow">
|
|
||||||
<td colspan="9" class="text-center text-muted py-4">
|
|
||||||
<?php if (!empty($search)): ?>
|
|
||||||
לא נמצאו שירים התואמים את החיפוש "<?php echo htmlspecialchars($search); ?>".
|
|
||||||
<?php else: ?>
|
|
||||||
עדיין אין שירים במאגר. <a href="#" id="addSongBtnLink">הוסף את השיר הראשון שלך!</a>
|
|
||||||
<?php endif; ?>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<?php else: ?>
|
|
||||||
<?php foreach ($songs as $index => $song): ?>
|
|
||||||
<tr>
|
|
||||||
<td><?php echo $index + 1; ?></td>
|
|
||||||
<td><?php echo htmlspecialchars($song['artist']); ?></td>
|
|
||||||
<td class="fw-bold"><?php echo htmlspecialchars($song['name']); ?></td>
|
|
||||||
<td><?php echo htmlspecialchars($song['bpm']); ?></td>
|
|
||||||
<td><?php echo htmlspecialchars($song['song_key']); ?></td>
|
|
||||||
<td><?php echo format_duration($song['duration_seconds']); ?></td>
|
|
||||||
<td>
|
|
||||||
<?php foreach(explode(',', $song['tags']) as $tag): ?>
|
|
||||||
<?php if(trim($tag)): ?>
|
|
||||||
<span class="badge bg-secondary bg-opacity-25 text-dark-emphasis"><?php echo htmlspecialchars(trim($tag)); ?></span>
|
|
||||||
<?php endif; ?>
|
|
||||||
<?php endforeach; ?>
|
|
||||||
</td>
|
|
||||||
<td><?php echo htmlspecialchars($song['notes']); ?></td>
|
|
||||||
<td>
|
|
||||||
<button class="btn btn-sm btn-outline-primary edit-btn" data-song='<?php echo htmlspecialchars(json_encode($song), ENT_QUOTES, 'UTF-8'); ?>'>
|
|
||||||
<i class="bi bi-pencil"></i>
|
|
||||||
</button>
|
|
||||||
<form action="songs.php" method="POST" class="d-inline" onsubmit="return confirm('האם אתה בטוח שברצונך למחוק את השיר?');">
|
|
||||||
<input type="hidden" name="action" value="delete">
|
|
||||||
<input type="hidden" name="id" value="<?php echo $song['id']; ?>">
|
|
||||||
<button type="submit" class="btn btn-sm btn-outline-danger">
|
|
||||||
<i class="bi bi-trash"></i>
|
|
||||||
</button>
|
|
||||||
</form>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<?php endforeach; ?>
|
|
||||||
<?php endif; ?>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Add/Edit Song Modal -->
|
|
||||||
<div class="modal fade" id="songModal" tabindex="-1" aria-labelledby="songModalLabel" aria-hidden="true">
|
|
||||||
<div class="modal-dialog modal-lg">
|
|
||||||
<div class="modal-content">
|
|
||||||
<form id="songForm" action="songs.php" method="POST">
|
|
||||||
<input type="hidden" name="action" id="action" value="create">
|
|
||||||
<input type="hidden" name="id" id="song_id">
|
|
||||||
<div class="modal-header">
|
|
||||||
<h5 class="modal-title" id="songModalLabel">הוספת שיר חדש</h5>
|
|
||||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
|
||||||
</div>
|
|
||||||
<div class="modal-body">
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-6 mb-3">
|
|
||||||
<label for="artist" class="form-label">שם האמן</label>
|
|
||||||
<input type="text" class="form-control" id="artist" name="artist">
|
|
||||||
</div>
|
|
||||||
<div class="col-md-6 mb-3">
|
|
||||||
<label for="name" class="form-label">שם השיר <span class="text-danger">*</span></label>
|
|
||||||
<input type="text" class="form-control" id="name" name="name" required>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-4 mb-3">
|
|
||||||
<label for="bpm" class="form-label">BPM</label>
|
|
||||||
<input type="number" class="form-control" id="bpm" name="bpm">
|
|
||||||
</div>
|
|
||||||
<div class="col-md-4 mb-3">
|
|
||||||
<label class="form-label">סולם</label>
|
|
||||||
<div class="input-group">
|
|
||||||
<select class="form-select" id="key_note" name="key_note">
|
|
||||||
<option value="" selected disabled>תו</option>
|
|
||||||
<option value="C">C</option>
|
|
||||||
<option value="C#">C#</option>
|
|
||||||
<option value="D">D</option>
|
|
||||||
<option value="D#">D#</option>
|
|
||||||
<option value="E">E</option>
|
|
||||||
<option value="F">F</option>
|
|
||||||
<option value="F#">F#</option>
|
|
||||||
<option value="G">G</option>
|
|
||||||
<option value="G#">G#</option>
|
|
||||||
<option value="A">A</option>
|
|
||||||
<option value="A#">A#</option>
|
|
||||||
<option value="B">B</option>
|
|
||||||
</select>
|
|
||||||
<select class="form-select" id="key_scale" name="key_scale">
|
|
||||||
<option value="Major">Major</option>
|
|
||||||
<option value="Minor">Minor</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col-md-4 mb-3">
|
|
||||||
<label class="form-label">משך (mm:ss)</label>
|
|
||||||
<div class="input-group duration-input-group">
|
|
||||||
<input type="number" class="form-control text-center" id="duration_minutes" name="duration_minutes" placeholder="00" min="0" max="59">
|
|
||||||
<span class="input-group-text">:</span>
|
|
||||||
<input type="number" class="form-control text-center" id="duration_seconds" name="duration_seconds" placeholder="00" min="0" max="59">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="mb-3">
|
|
||||||
<label for="tags" class="form-label">תגים / סגנון</label>
|
|
||||||
<input type="text" class="form-control" id="tags" name="tags" placeholder="מופרד בפסיקים, לדוגמה: רוק, קאבר, שקט">
|
|
||||||
</div>
|
|
||||||
<div class="mb-3">
|
|
||||||
<label for="notes" class="form-label">הערות</label>
|
|
||||||
<textarea class="form-control" id="notes" name="notes" rows="3"></textarea>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="modal-footer">
|
|
||||||
<button type="button" class="btn btn-light" data-bs-dismiss="modal">ביטול</button>
|
|
||||||
<button type="submit" class="btn btn-primary">שמור שיר</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
<form id="deleteForm" action="songs.php" method="POST" class="d-none">
|
|
||||||
<input type="hidden" name="action" value="delete">
|
|
||||||
<input type="hidden" name="id">
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<script src="assets/js/songs_page.js?v=<?php echo time(); ?>"></script>
|
|
||||||
<?php include 'includes/footer.php'; ?>
|
|
||||||
Loading…
x
Reference in New Issue
Block a user