Files
Fitness/web/photos/photos.php
T
2026-10-23 03:25:41 -07:00

314 lines
11 KiB
PHP

<?php
/**
* photos.php - Service Provider Photo Gallery Implementation
* Requirements:
* 1. Upload multiple images via PHP Frontend
* 2. Display gallery with thumbnail selection
* 3. Use Flask API for storage and metadata
* 4. Store photos in file system (not database BLOBs)
*/
require_once __DIR__ . '/config.php';
// Validate provider ID
if (!isset($_GET['id']) || empty($_GET['id'])) {
die('Provider ID missing. Use: photos.php?id=123');
}
$provider_id = $_GET['id'];
// Fetch existing photos from API
$api_url = BASE_API_URL . '/providers/' . urlencode($provider_id) . "/photos";
$existing_photos = [];
try {
$response = file_get_contents($api_url);
$existing_photos = json_decode($response, true);
if (isset($existing_photos['error'])) {
trigger_error($existing_photos['error'], E_USER_WARNING);
$existing_photos = [];
}
} catch (Exception $e) {
trigger_error('API error: ' . $e->getMessage(), E_USER_WARNING);
}
// Check upload permissions
$has_upload_perm = true; // Add authorization logic here
// Generate unique token for upload (security)
$upload_token = bin2hex(random_bytes(16));
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Photos for Service Provider #<?php echo htmlspecialchars($provider_id); ?></title>
<style>
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Arial, sans-serif;
line-height: 1.5;
color: #333;
max-width: 1200px;
margin: 0 auto;
padding: 20px;
}
h1 {
margin-bottom: 30px;
border-bottom: 1px solid #eee;
padding-bottom: 15px;
}
.upload-section {
display: flex;
align-items: center;
gap: 15px;
margin: 20px 0;
}
.upload-btn {
padding: 10px 20px;
background: #3a86ff;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
}
.upload-btn:hover {
background: #1d66cc;
}
#preview-container {
display: none;
gap: 10px;
margin-top: 10px;
}
.photo-item {
position: relative;
width: 80px;
height: 80px;
margin: 5px;
}
.photo-item img {
width: 100%;
height: 100%;
object-fit: cover;
border-radius: 4px;
}
.main-indicator {
position: absolute;
top: 2px;
right: 2px;
width: 16px;
height: 16px;
background: #3a86ff;
border-radius: 50%;
}
#gallery {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
gap: 20px;
margin-top: 30px;
}
.loading {
text-align: center;
padding: 20px;
color: #666;
}
.no-photos {
text-align: center;
padding: 50px;
color: #777;
}
.error {
color: #e74c3c;
margin: 20px 0;
}
</style>
</head>
<body>
<h1>Photos for Service Provider #<?php echo htmlspecialchars($provider_id); ?></h1>
<?php if (isset($existing_photos['error'])): ?>
<div class="error">
<?php echo htmlspecialchars($existing_photos['error']); ?>
</div>
<?php endif; ?>
<?php if ($has_upload_perm): ?>
<div class="upload-section">
<input type="file" id="photo-upload" name="photos[]" multiple accept="image/*">
<button id="upload-btn" class="upload-btn">Upload Photos</button>
</div>
<div id="preview-container"></div>
<?php else: ?>
<p>Upload permissions disabled. Contact administrator.</p>
<?php endif; ?>
<div class="loading" id="loading">
Loading photos...
</div>
<div id="gallery">
<?php if (empty($existing_photos)): ?>
<div class="no-photos">
<h2>No photos uploaded yet</h2>
<p>Upload images to display them here</p>
</div>
<?php else: ?>
<?php foreach ($existing_photos as $photo): ?>
<div class="photo-item">
<input type="radio" name="main_photo" value="<?php echo htmlspecialchars($photo['path']); ?>" <?php echo $photo['is_main'] ? 'checked' : ''; ?> class="main-radio">
<img src="<?php echo htmlspecialchars($photo['path']); ?>" alt="Service provider photo">
<?php if ($photo['is_main']): ?>
<div class="main-indicator"></div>
<?php endif; ?>
</div>
<?php endforeach; ?>
<?php endif; ?>
</div>
<script>
document.addEventListener('DOMContentLoaded', () => {
const photoInput = document.getElementById('photo-upload');
const previewContainer = document.getElementById('preview-container');
const gallery = document.getElementById('gallery');
const loading = document.getElementById('loading');
// Show preview on file selection
photoInput.addEventListener('change', function(e) {
if (this.files.length > 0) {
previewContainer.style.display = 'flex';
}
previewContainer.innerHTML = '';
for (let i = 0; i < this.files.length; i++) {
const file = this.files[i];
const reader = new FileReader();
reader.onload = function(e) {
const preview = document.createElement('div');
preview.className = 'photo-item';
preview.innerHTML = `
<input type="radio" name="main_photo" value="${file.name}"
class="main-radio"
style="position: absolute; top: 2px; left: 2px;">
<img src="${e.target.result}"
alt="Photo preview">
<div style="position: absolute; top: 2px; right: 2px;
width: 16px; height: 16px;
background: #3a86ff;
border-radius: 50%;"></div>
`;
previewContainer.appendChild(preview);
};
reader.readAsDataURL(file);
}
});
// Handle upload button
document.getElementById('upload-btn').addEventListener('click', async () => {
const fileInput = document.getElementById('photo-upload');
if (!fileInput.files.length) {
alert('Please select at least one photo to upload');
return;
}
const formData = new FormData();
for (let i = 0; i < fileInput.files.length; i++) {
formData.append('photos[]', fileInput.files[i]);
}
try {
const response = await fetch(
BASE_API_URL . '/providers/<?php echo urlencode($provider_id); ?>/photos',
{
method: 'POST',
body: formData,
headers: {
'X-Upload-Token': '<?php echo $upload_token; ?>'
}
}
);
if (!response.ok) {
throw new Error(`API error: ${response.status}`);
}
const data = await response.json();
// Add new photos to gallery
data.paths.forEach(path => {
const newPhoto = document.createElement('div');
newPhoto.className = 'photo-item';
newPhoto.innerHTML = `
<input type="radio" name="main_photo" value="${path}"
class="main-radio"
style="position: absolute; top: 2px; left: 2px;">
<img src="${path}"
alt="New photo"
style="object-fit: cover;">
<div style="position: absolute; top: 2px; right: 2px;
width: 16px; height: 16px;
background: #3a86ff;
border-radius: 50%;"></div>
`;
gallery.appendChild(newPhoto);
});
// Reset form
fileInput.value = '';
previewContainer.style.display = 'none';
// Update main photo selection (if any)
const mainRadios = gallery.querySelectorAll('.main-radio');
if (mainRadios.length > 0) {
mainRadios[0].checked = true;
}
} catch (error) {
console.error('Upload failed:', error);
alert('Upload failed: ' + error.message);
}
});
// Handle main photo selection
gallery.addEventListener('change', async (e) => {
if (e.target.className === 'main-radio') {
const mainPhoto = e.target.value;
const providerId = '<?php echo $provider_id; ?>';
try {
const response = await fetch(
BASE_API_URL . '/providers/${providerId}/main_photo',
{
method: 'PUT',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
photo_path: mainPhoto
})
}
);
if (!response.ok) {
throw new Error(`API error: ${response.status}`);
}
// Update visuals
gallery.querySelectorAll('.main-indicator').forEach(ind => {
ind.style.display = 'none';
});
e.target.parentElement.querySelector('.main-indicator').style.display = 'block';
} catch (error) {
console.error('Setting main photo failed:', error);
}
}
});
// Hide loading message
loading.style.display = 'none';
});
</script>
</body>
</html>