314 lines
11 KiB
PHP
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>
|