moving helper functions to scripts folder
This commit is contained in:
1 parent
e7fad500f9
commit
dc39b6988c
9 files changed
+525
-60
No files matched your search
+59
-19
@@ -3,6 +3,15 @@ from database import *
|
||||
from werkzeug.utils import secure_filename
|
||||
|
||||
app = Flask(__name__)
|
||||
app.config['UPLOAD_FOLDER'] = os.path.join(app.root_path, 'photos')
|
||||
app.config['URL_PREFIX'] = '/uploads' # public URL prefix
|
||||
|
||||
# make sure the folder exists
|
||||
os.makedirs(app.config['UPLOAD_FOLDER'], exist_ok=True)
|
||||
|
||||
# helper to build a public URL
|
||||
def public_url(filename: str) -> str:
|
||||
return f"{app.config['URL_PREFIX']}/{filename}"
|
||||
|
||||
# Initialise database on startup
|
||||
initialize_db()
|
||||
@@ -179,29 +188,60 @@ def setting_detail(name: str):
|
||||
|
||||
# ----------------------------- Photos -----------------------------
|
||||
|
||||
@app.route('/api/providers/<provider_id>/photos', methods=['POST'])
|
||||
# photo upload endpoint
|
||||
@app.route('/api/providers/<int:provider_id>/photos', methods=['POST'])
|
||||
def upload_photos(provider_id):
|
||||
if 'photos' not in request.files:
|
||||
return jsonify({'error': 'No files uploaded'}), 400
|
||||
|
||||
# Secure filename + save to persistent storage
|
||||
upload_folder = f'{os.path.dirname(__file__)}/photos'
|
||||
os.makedirs(upload_folder, exist_ok=True)
|
||||
|
||||
photo_paths = []
|
||||
for file in request.files.getlist('photos'):
|
||||
if file.filename == '':
|
||||
|
||||
files = request.files.getlist('photos')
|
||||
if not files:
|
||||
return jsonify({'error': 'No files in list'}), 400
|
||||
|
||||
allowed_ext = {'.jpg', '.jpeg', '.png', '.gif'}
|
||||
saved_filenames = []
|
||||
|
||||
for f in files:
|
||||
if f.filename == '':
|
||||
continue
|
||||
|
||||
filename = secure_filename(file.filename)
|
||||
file.save(os.path.join(upload_folder, filename))
|
||||
photo_paths.append(f"/uploads/{filename}")
|
||||
|
||||
# Store paths in DB (e.g., PostgreSQL JSON field)
|
||||
db.execute("UPDATE providers SET photos = %s WHERE id = %s",
|
||||
(json.dumps(photo_paths), provider_id))
|
||||
|
||||
return jsonify({'success': True, 'paths': photo_paths}), 200
|
||||
filename = secure_filename(f.filename)
|
||||
ext = os.path.splitext(filename)[1].lower()
|
||||
if ext not in allowed_ext:
|
||||
return jsonify({'error': f'Unsupported file type: {ext}'}), 400
|
||||
# rename to avoid collisions
|
||||
new_name = f"{uuid.uuid4().hex}{ext}"
|
||||
f.save(os.path.join(app.config['UPLOAD_FOLDER'], new_name))
|
||||
saved_filenames.append(public_url(new_name))
|
||||
|
||||
# Merge with any pre‑existing photos
|
||||
existing = db.fetch(f"SELECT photos FROM providers WHERE id=?", (provider_id,))[0][0]
|
||||
try:
|
||||
photos = json.loads(existing)
|
||||
except Exception:
|
||||
photos = []
|
||||
photos.extend(saved_filenames)
|
||||
|
||||
db.execute("UPDATE providers SET photos = %s WHERE id = %s",
|
||||
(json.dumps(photos), provider_id))
|
||||
|
||||
return jsonify({'success': True, 'photos': photos}), 200
|
||||
|
||||
# Endpoint to change the main photo
|
||||
@app.route('/api/providers/<int:provider_id>/main_photo', methods=['PUT'])
|
||||
def set_main_photo(provider_id):
|
||||
data = request.get_json()
|
||||
url = data.get('url')
|
||||
if not url:
|
||||
return jsonify({'error': 'Missing url'}), 400
|
||||
|
||||
# Validate that the URL belongs to this provider
|
||||
photos = db.fetch(f"SELECT photos FROM providers WHERE id=?", (provider_id,))[0][0]
|
||||
if json.loads(photos).count(url) == 0:
|
||||
return jsonify({'error': 'URL not part of this provider'}), 400
|
||||
|
||||
db.execute("UPDATE providers SET main_photo = %s WHERE id = %s",
|
||||
(url, provider_id))
|
||||
return jsonify({'success': True, 'main_photo': url})
|
||||
|
||||
# Run the app if this file is executed directly
|
||||
if __name__ == "__main__":
|
||||
|
||||
+3
-1
@@ -52,7 +52,9 @@ def initialize_db() -> None:
|
||||
age INTEGER,
|
||||
photo TEXT,
|
||||
rates TEXT,
|
||||
private INTEGER NOT NULL DEFAULT 0
|
||||
private INTEGER NOT NULL DEFAULT 0,
|
||||
photos TEXT DEFAULT '[]',
|
||||
main_photo TEXT
|
||||
);
|
||||
"""
|
||||
)
|
||||
|
||||
+307
-30
@@ -1,37 +1,314 @@
|
||||
<html>
|
||||
<head>
|
||||
</head>
|
||||
<?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';
|
||||
|
||||
<body>
|
||||
// Validate provider ID
|
||||
if (!isset($_GET['id']) || empty($_GET['id'])) {
|
||||
die('Provider ID missing. Use: photos.php?id=123');
|
||||
}
|
||||
$provider_id = $_GET['id'];
|
||||
|
||||
<div class="photo-grid">
|
||||
<div class="photo-item">
|
||||
<input type="radio" name="main_photo" value="1" checked>
|
||||
<img src="photo1.jpg" />
|
||||
// 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>
|
||||
<div class="photo-item">
|
||||
<input type="radio" name="main_photo" value="2">
|
||||
<img src="photo2.jpg" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<input type="file" id="photo-upload" name="photos[]" multiple accept="image/*" />
|
||||
<div id="preview-container"></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; ?>
|
||||
|
||||
<script>
|
||||
document.getElementById('photo-upload').addEventListener('change', function(e) {
|
||||
const files = Array.from(e.target.files);
|
||||
const preview = document.getElementById('preview-container');
|
||||
preview.innerHTML = '';
|
||||
|
||||
files.forEach(file => {
|
||||
const img = document.createElement('img');
|
||||
img.src = URL.createObjectURL(file);
|
||||
img.classList.add('preview-thumb');
|
||||
preview.appendChild(img);
|
||||
<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);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
// 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>
|
||||
File renamed without changes.
File renamed without changes.
@@ -33,7 +33,39 @@ function callApi(string $method, string $path, $body = null)
|
||||
}
|
||||
// ------------------------------------------------------------------
|
||||
|
||||
// 1️⃣ Get provider details
|
||||
// ------------------------------------------------------------------
|
||||
// Helper: upload photos (multipart)
|
||||
// Returns the JSON response from the API (or null on error)
|
||||
function uploadPhotosApi(int $provider_id, array $files): ?array
|
||||
{
|
||||
$url = BASE_API_URL . "/api/providers/{$provider_id}/photos";
|
||||
|
||||
$ch = curl_init($url);
|
||||
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
|
||||
// Build multipart array
|
||||
$multipart = [];
|
||||
foreach ($files['tmp_name'] as $i => $tmp) {
|
||||
if ($files['name'][$i] === '') continue;
|
||||
$multipart[] = [
|
||||
'name' => 'photos',
|
||||
'contents' => fopen($tmp, 'r'),
|
||||
'filename' => $files['name'][$i]
|
||||
];
|
||||
}
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, $multipart);
|
||||
// Let PHP set the correct Content‑Type boundary
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: multipart/form-data']);
|
||||
|
||||
$response = curl_exec($ch);
|
||||
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
curl_close($ch);
|
||||
|
||||
return ($httpCode < 400) ? json_decode($response, true) : null;
|
||||
}
|
||||
|
||||
// Get provider details
|
||||
$provider = callApi('GET', '/providers?id=' . $provider_id);
|
||||
if (!$provider) {
|
||||
echo '<h1>Error</h1><p>Could not load provider details.</p>';
|
||||
@@ -43,14 +75,14 @@ if (is_array($provider) && count($provider) > 0 && isset($provider[0])) {
|
||||
$provider = $provider[0];
|
||||
}
|
||||
|
||||
// 2️⃣ Get rate‑option list (comma‑separated string → array)
|
||||
// Get rate‑option list (comma‑separated string → array)
|
||||
$rateSetting = callApi('GET', '/settings/rate_options');
|
||||
$rateOptions = [];
|
||||
if ($rateSetting && isset($rateSetting['value'])) {
|
||||
$rateOptions = array_map('trim', explode(',', $rateSetting['value']));
|
||||
}
|
||||
|
||||
// 3️⃣ Decode the API’s rate string (JSON → array of arrays)
|
||||
// Decode the API’s rate string (JSON → array of arrays)
|
||||
$ratesData = $provider['rates'] ?? '[]';
|
||||
$decoded = [];
|
||||
if (is_string($ratesData)) {
|
||||
@@ -62,7 +94,7 @@ if (is_string($ratesData)) {
|
||||
$decoded = $ratesData; // already an array – legacy path
|
||||
}
|
||||
|
||||
// 4️⃣ Build two helper arrays
|
||||
// Build two helper arrays
|
||||
$selectedRateKeys = []; // lowercase keys of selected rates
|
||||
$ratesAssoc = []; // key → value
|
||||
|
||||
@@ -75,7 +107,7 @@ foreach ($decoded as $entry) {
|
||||
}
|
||||
}
|
||||
|
||||
// 5️⃣ Find orphan rates (present in $decoded but not in $rateOptions)
|
||||
// Find orphan rates (present in $decoded but not in $rateOptions)
|
||||
$orphanRates = [];
|
||||
$orphanKeys = [];
|
||||
foreach ($decoded as $entry) {
|
||||
@@ -90,8 +122,8 @@ foreach ($decoded as $entry) {
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// 6️⃣ Handle form POST
|
||||
// --- Handle form POST -------------------------------------------------
|
||||
// Handle form POST
|
||||
/*
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
// Base payload
|
||||
$payload = [
|
||||
@@ -107,14 +139,14 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
// Rebuild the rates array from the form.
|
||||
$finalRates = [];
|
||||
|
||||
// 1️⃣ Add the selected valid rate options (value may be changed by the user)
|
||||
// Add the selected valid rate options (value may be changed by the user)
|
||||
$selectedRateKeysRaw = $_POST['rate_selected'] ?? [];
|
||||
foreach ($selectedRateKeysRaw as $opt) { // $opt is the original rate name
|
||||
$newVal = $_POST['rate_value'][$opt] ?? '0'; // the updated value
|
||||
$finalRates[] = [$opt => $newVal];
|
||||
}
|
||||
|
||||
// 2️⃣ Add orphan rates that the user chose **not** to delete
|
||||
// Add orphan rates that the user chose **not** to delete
|
||||
$orphanDeleteSet = array_flip(array_map('strtolower', $_POST['orphan_delete'] ?? []));
|
||||
foreach ($orphanRates as $key => $value) { // $key is already lower‑cased
|
||||
if (!isset($orphanDeleteSet[strtolower($key)])) {
|
||||
@@ -122,7 +154,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
}
|
||||
}
|
||||
|
||||
// 3️⃣ Encode the new array as JSON
|
||||
// Encode the new array as JSON
|
||||
//$payload['rates'] = !empty($finalRates) ? json_encode($finalRates) : null;
|
||||
$payload['rates'] = !empty($finalRates) ? $finalRates : null;
|
||||
|
||||
@@ -135,6 +167,68 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
exit;
|
||||
}
|
||||
echo '<h1>Error</h1><p>Failed to update provider.</p>';
|
||||
}*/
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
|
||||
/* -------- 1. Build the base payload -------- */
|
||||
$payload = [
|
||||
'name' => $_POST['name'] ?? '',
|
||||
'age' => (int)($_POST['age'] ?? 0),
|
||||
'photo' => $_POST['photo'] ?? '',
|
||||
'phone' => $_POST['phone'] ?? '',
|
||||
'private' => isset($_POST['private']),
|
||||
];
|
||||
|
||||
/* -------- 2. Re‑build the rates array -------- */
|
||||
$finalRates = [];
|
||||
$selectedRateKeysRaw = $_POST['rate_selected'] ?? [];
|
||||
foreach ($selectedRateKeysRaw as $opt) {
|
||||
$newVal = $_POST['rate_value'][$opt] ?? '0';
|
||||
$finalRates[] = [$opt => $newVal];
|
||||
}
|
||||
// orphan rates that the user chose *not* to delete
|
||||
$orphanDeleteSet = array_flip(array_map('strtolower', $_POST['orphan_delete'] ?? []));
|
||||
foreach ($orphanRates as $key => $value) {
|
||||
if (!isset($orphanDeleteSet[$key])) {
|
||||
$finalRates[] = [$key => $value];
|
||||
}
|
||||
}
|
||||
$payload['rates'] = !empty($finalRates) ? $finalRates : null;
|
||||
|
||||
/* -------- 3. Call the API to update provider -------- */
|
||||
$result = callApi('PUT', "/providers/{$provider_id}", $payload);
|
||||
if (!$result) {
|
||||
echo '<h1>Error</h1><p>Failed to update provider.</p>';
|
||||
exit;
|
||||
}
|
||||
|
||||
/* -------- 4. Upload photos if any were chosen -------- */
|
||||
if (!empty($_FILES['photos']['name'][0])) {
|
||||
$uploadRes = uploadPhotosApi($provider_id, $_FILES['photos']);
|
||||
if ($uploadRes && isset($uploadRes['photos'])) {
|
||||
// Update main photo if the user selected one
|
||||
$selectedMain = $_POST['main_photo'] ?? null;
|
||||
if ($selectedMain) {
|
||||
callApi('PUT', "/api/providers/{$provider_id}/main_photo",
|
||||
['url' => $selectedMain]);
|
||||
}
|
||||
} else {
|
||||
// Photo upload failed – you may want to show a message
|
||||
echo '<p style="color:red;">Photo upload failed.</p>';
|
||||
}
|
||||
}
|
||||
|
||||
/* -------- 5. Set the chosen main photo from the new uploads -------- */
|
||||
// If a main photo was chosen from the new uploads, send it
|
||||
if (!empty($_POST['main_photo'])) {
|
||||
callApi('PUT', "/api/providers/{$provider_id}/main_photo",
|
||||
['url' => $_POST['main_photo']]);
|
||||
}
|
||||
|
||||
/* -------- 6. Redirect back to the list -------- */
|
||||
header('Location: list_providers.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
?>
|
||||
@@ -158,6 +252,30 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
<h1>Edit Provider</h1>
|
||||
<form method="post" id="editForm">
|
||||
<!-- Basic fields -->
|
||||
|
||||
<?php if (!empty($provider['photos'])): ?>
|
||||
<fieldset>
|
||||
<legend>Existing Photos</legend>
|
||||
<div style="display:flex; flex-wrap:wrap;">
|
||||
<?php foreach ($provider['photos'] as $idx => $url): ?>
|
||||
<div style="margin:4px; text-align:center;">
|
||||
<img src="<?= htmlspecialchars($url) ?>" style="max-width:100px; height:auto;">
|
||||
<br>
|
||||
<input type="radio" name="main_photo" value="<?= htmlspecialchars($url) ?>"
|
||||
<?= ($provider['main_photo'] ?? '') === $url ? 'checked' : '' ?>>
|
||||
<label>Thumbnail</label>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
</fieldset>
|
||||
<?php endif; ?>
|
||||
<fieldset>
|
||||
<legend>Upload New Photos</legend>
|
||||
<label>
|
||||
<input type="file" name="photos[]" multiple accept="image/*">
|
||||
</label>
|
||||
<p>You can select several images at once.</p>
|
||||
</fieldset>
|
||||
<label>Name: <input type="text" name="name" required value="<?php echo htmlspecialchars($provider['name'] ?? ''); ?>"></label>
|
||||
<label>Age: <input type="number" name="age" required value="<?php echo htmlspecialchars($provider['age'] ?? ''); ?>"></label>
|
||||
<label>Photo URL: <input type="text" name="photo" value="<?php echo htmlspecialchars($provider['photo'] ?? ''); ?>"></label>
|
||||
File renamed without changes.
File renamed without changes.
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
require_once __DIR__.'/config.php';
|
||||
|
||||
function uploadPhotos($provider_id, $files) {
|
||||
$url = BASE_API_URL . "/api/providers/$provider_id/photos";
|
||||
|
||||
$c = curl_init($url);
|
||||
curl_setopt($c, CURLOPT_POST, true);
|
||||
curl_setopt($c, CURLOPT_HTTPHEADER, ['Content-Type: multipart/form-data']);
|
||||
// Build multipart array
|
||||
$multipart = [];
|
||||
foreach ($files['tmp_name'] as $i => $tmp) {
|
||||
$multipart[] = [
|
||||
'name' => 'photos',
|
||||
'contents' => fopen($tmp, 'r'),
|
||||
'filename' => $files['name'][$i],
|
||||
];
|
||||
}
|
||||
curl_setopt($c, CURLOPT_POSTFIELDS, $multipart);
|
||||
curl_setopt($c, CURLOPT_RETURNTRANSFER, true);
|
||||
|
||||
$res = curl_exec($c);
|
||||
$http = curl_getinfo($c, CURLINFO_HTTP_CODE);
|
||||
curl_close($c);
|
||||
|
||||
return $http < 400 ? json_decode($res, true) : null;
|
||||
}
|
||||
?>
|
||||
Reference in new issue
Block a user