add username to list_providers

This commit is contained in:
matt committed 2026-10-28 22:24:12 -07:00
1 parent 9e4c6571cf
commit 9dc05a72b5
5 files changed
+9 -1

No files matched your search

+146
View File
@@ -0,0 +1,146 @@
<?php
// add_providers.php
// Simple page that displays a form to create a new fitness provider.
// It uses the same Flask API as list_providers.php (http://172.25.1.105:5000).
// When the form is submitted, it POSTs JSON to /providers.
// file settings
require_once __DIR__ . '/../fitness-config.php';
// Helper to call the API -------------------------------------------
function callApi(string $method, string $path, $body = null)
{
$url = BASE_API_URL . $path;
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
if ($body !== null) {
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($body));
}
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($httpCode >= 400) {
return null; // or throw an exception
}
return json_decode($response, true);
}
// Fetch rate options setting
$rateOptionsSetting = callApi('GET', '/settings/rate_options');
$rateOptions = [];
if ($rateOptionsSetting && isset($rateOptionsSetting['value'])) {
$rateOptions = explode(',', $rateOptionsSetting['value']);
/*
$parsed = json_decode($rateOptionsSetting['value'], true);
if (is_array($parsed)) {
$rateOptions = $parsed;
}
echo "Rate options:".$rateOptionsSetting['value'];
*/
}
// Update form handling to process selected rates
$selectedRateKeys = $_POST['rate_selected'] ?? [];
$ratesArray = [];
foreach ($selectedRateKeys as $key) {
$value = $_POST['rate_value'][$key] ?? '0';
$ratesArray[] = [$key => $value];
}
$created = null;
$errors = [];
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
// Collect form data
$name = trim($_POST['name'] ?? '');
$phone = trim($_POST['phone'] ?? '');
$age = trim($_POST['age'] ?? '');
$photo = trim($_POST['photo'] ?? '');
$private = isset($_POST['private']) ? true : false;
// Basic validation
if ($name === '') { $errors[] = 'Name is required.'; }
if ($age !== '' && !is_numeric($age)) { $errors[] = 'Age must be a number.'; }
// Rates now processed from checkboxes
if (empty($errors)) {
$body = [
'name' => $name,
'phone' => $phone,
'age' => $age === '' ? null : (int)$age,
'photo' => $photo === '' ? null : $photo,
'rates' => empty($ratesArray) ? null : $ratesArray,
'private'=> $private
];
$result = callApi('POST', '/providers', $body);
if ($result) {
// On successful creation, redirect to the list providers page
header('Location: list_providers.php');
exit;
} else {
$errors[] = 'Failed to create provider. API error.';
}
}
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Add Provider</title>
<link rel="stylesheet" href="https://cosmostat.matt-cloud.com/src/styles.css">
<style>
body { font-family: Arial, sans-serif; margin: 2rem; }
form { max-width: 600px; }
label { display: block; margin-top: 1rem; }
input[type=text], input[type=number] { width: 100%; padding: 0.5rem; }
.error { color: red; }
.success { color: green; }
</style>
</head>
<body>
<div class="card">
<h1>Add New Provider</h1>
<?php if ($errors): ?>
<div class="error">
<?php foreach ($errors as $e): ?>
<p><?=htmlspecialchars($e)?></p>
<?php endforeach; ?>
</div>
<?php endif; ?>
<form method="post" action="">
<?php
// echo $rateOptions[0];
?>
<label>Name:<br><input type="text" name="name" value="<?=htmlspecialchars($_POST['name'] ?? '')?>" required></label>
<label>Phone:<br><input type="text" name="phone" value="<?=htmlspecialchars($_POST['phone'] ?? '')?>"></label>
<label>Age:<br><input type="number" name="age" value="<?=htmlspecialchars($_POST['age'] ?? '')?>"></label>
<label>Photo URL:<br><input type="text" name="photo" value="<?=htmlspecialchars($_POST['photo'] ?? '')?>"></label>
<?php if (!empty($rateOptions)) : ?>
<label>Rate Options:<br>
<?php
foreach ($rateOptions as $option) :
// echo $option;
$key = $option;
$value = 0;
?>
<input type="checkbox" name="rate_selected[]" value="<?=htmlspecialchars($key)?>"
<?php if (in_array($key, $selectedRateKeys)) echo 'checked'; ?>>
<?=htmlspecialchars($key)?>:
<input type="number" name="rate_value[<?=htmlspecialchars($key)?>]" value="<?=htmlspecialchars($ratesArray[$key] ?? $value)?>" style="width:60px;">
<br>
<?php endforeach; ?>
</label>
<?php endif; ?>
<label><input type="checkbox" name="private" <?=isset($_POST['private'])?'checked':''?>> Private</label><br>
<button type="submit" class="btn">Create Provider</button>
</form>
</div>
</body>
</html>
+321
View File
@@ -0,0 +1,321 @@
<?php
// edit_provider.php
// Edit an existing fitness provider.
// Handles rate options similar to add_provider.php.
require_once __DIR__ . '/../fitness-config.php';
$provider_id = (int)($_GET['id'] ?? 0);
if ($provider_id <= 0) {
echo '<h1>Error</h1><p>Provider ID is missing or invalid.</p>';
exit;
}
// ------------------------------------------------------------------
// Helper: Call the API (GET/PUT) – returns decoded JSON or null
function callApi(string $method, string $path, $body = null)
{
$url = BASE_API_URL . $path;
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
if ($body !== null) {
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($body));
}
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($httpCode >= 400) {
return null;
}
return json_decode($response, true);
}
// ------------------------------------------------------------------
// ------------------------------------------------------------------
// 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>';
exit;
}
if (is_array($provider) && count($provider) > 0 && isset($provider[0])) {
$provider = $provider[0];
}
// 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']));
}
// Decode the API’s rate string (JSON → array of arrays)
$ratesData = $provider['rates'] ?? '[]';
$decoded = [];
if (is_string($ratesData)) {
$decoded = json_decode($ratesData, true);
if (json_last_error() !== JSON_ERROR_NONE) {
$decoded = []; // bad JSON – treat as empty
}
} elseif (is_array($ratesData)) {
$decoded = $ratesData; // already an array – legacy path
}
// Build two helper arrays
$selectedRateKeys = []; // lowercase keys of selected rates
$ratesAssoc = []; // key → value
foreach ($decoded as $entry) {
if (!is_array($entry)) continue;
foreach ($entry as $k => $v) {
$kTrim = strtolower(trim($k));
$selectedRateKeys[] = $kTrim;
$ratesAssoc[$kTrim] = $v;
}
}
// Find orphan rates (present in $decoded but not in $rateOptions)
$orphanRates = [];
$orphanKeys = [];
foreach ($decoded as $entry) {
if (!is_array($entry)) continue;
foreach ($entry as $k => $v) {
$kTrim = strtolower(trim($k));
if (!in_array($kTrim, array_map('strtolower', $rateOptions))) {
$orphanRates[$kTrim] = $v;
$orphanKeys[] = $kTrim;
}
}
}
// ------------------------------------------------------------------
// Handle form POST
/*
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
// Base payload
$payload = [
'name' => $_POST['name'] ?? '',
'age' => (int)($_POST['age'] ?? 0),
'photo' => $_POST['photo'] ?? '',
'phone' => $_POST['phone'] ?? '',
// Convert checkbox to boolean
'private' => isset($_POST['private']),
];
// ------------------------------------------------------------------
// Rebuild the rates array from the form.
$finalRates = [];
// 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];
}
// 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)])) {
$finalRates[] = [$key => $value];
}
}
// Encode the new array as JSON
//$payload['rates'] = !empty($finalRates) ? json_encode($finalRates) : null;
$payload['rates'] = !empty($finalRates) ? $finalRates : null;
// ------------------------------------------------------------------
// Submit the updated provider
$result = callApi('PUT', '/providers/' . $provider_id, $payload);
if ($result) {
header('Location: list_providers.php');
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;
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Edit Provider</title>
<link rel="stylesheet" href="https://cosmostat.matt-cloud.com/src/styles.css">
<style>
body {font-family: Arial, sans-serif; margin: 20px;}
form {max-width: 600px;}
label {display: block; margin-top: 10px;}
input[type=text], input[type=number], textarea {width: 100%; padding: 8px; box-sizing: border-box;}
input[type=checkbox] {margin-top: 5px;}
button {margin-top: 15px; padding: 10px 15px;}
</style>
</head>
<body>
<div class="card">
<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>
<label>Phone: <input type="text" name="phone" required value="<?php echo htmlspecialchars($provider['phone'] ?? ''); ?>"></label>
<!-- Rate options -->
<?php if (!empty($rateOptions)) : ?>
<label>Rate Options:<br>
<?php foreach ($rateOptions as $opt) :
$key = strtolower(trim($opt));
$value = $ratesAssoc[$key] ?? 0;
?>
<input type="checkbox" name="rate_selected[]" value="<?php echo htmlspecialchars($opt); ?>"
<?php if (in_array($key, $selectedRateKeys)) echo 'checked'; ?> >
<?php echo htmlspecialchars($opt); ?>:
<input type="number" name="rate_value[<?php echo htmlspecialchars($opt); ?>]" value="<?php echo htmlspecialchars($value); ?>" style="width:60px;">
<br>
<?php endforeach; ?>
</label>
<?php endif; ?>
<!-- Orphan rates – show value and a Delete checkbox -->
<?php if (!empty($orphanRates)) : ?>
<label>Orphan Rates:<br>
<?php foreach ($orphanRates as $key => $value) : ?>
<?php echo htmlspecialchars($key); ?>:
<input type="number" value="<?php echo htmlspecialchars($value); ?>" style="width:60px;" disabled>
<input type="checkbox" name="orphan_delete[]" value="<?php echo htmlspecialchars($key); ?>" style="margin-left:10px;"> Delete
<br>
<?php endforeach; ?>
</label>
<?php endif; ?>
<!-- Private flag -->
<label><input type="checkbox" name="private" <?php echo (!empty($provider['private'])) ? 'checked' : ''; ?>> Private</label><br>
<!-- Buttons -->
<button type="submit">Save</button>
<button type="button" onclick="window.location.href='list_providers.php'">Cancel</button>
</form>
</div>
</body>
</html>
+1 -1
View File
@@ -44,7 +44,7 @@ if ($debug_output) {
<body>
<?php
include_once('../scripts/list_providers.php');
include_once('list_providers.php');
?>
</body>
</html>
+187
View File
@@ -0,0 +1,187 @@
<?php
// providers.php
// Simple page that pulls provider data via the Flask API.
// It expects the Flask API to be reachable at http://127.0.0.1:5000
// The API endpoint for providers is /providers.
// file settings
require_once __DIR__ . '/../fitness-config.php';
// --- Helper to call the API -------------------------------------------
function callApi(string $method, string $path, $body = null)
{
$url = BASE_API_URL . $path;
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
if ($body !== null) {
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($body));
}
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($httpCode >= 400) {
return null; // or throw an exception
}
return json_decode($response, true);
}
// --- Fetch all providers -----------------------------------------------
$providers = callApi('GET', '/providers');
$providers = $providers ?? [];
// Normalize rates for display
foreach ($providers as &$p) {
$rates = $p['rates'] ?? '';
if (is_string($rates)) {
$decoded = json_decode($rates, true);
if (json_last_error() !== JSON_ERROR_NONE) {
// try stripping slashes
$unescaped = stripslashes($rates);
$decoded = json_decode($unescaped, true);
}
if (json_last_error() === JSON_ERROR_NONE && is_array($decoded)) {
$p['rates'] = $decoded;
} else {
// leave as is
$p['rates'] = $rates;
}
} elseif (is_array($rates)) {
$p['rates'] = $rates;
}
}
unset($p);
// -----------------------------------------------------------------------
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Providers</title>
<link rel="stylesheet" href="https://cosmostat.matt-cloud.com/src/styles.css">
<style>
body { font-family: Arial, sans-serif; margin: 2rem; }
#search { margin-bottom: 1rem; padding: 0.5rem; width: 300px; }
table { width: 100%; border-collapse: collapse; }
img { max-width: 60px; height: auto; }
.header-container { display: flex; justify-content: space-between; align-items: center; margin-bottom: 1rem; }
</style>
</head>
<body>
<div class="card">
<div class="header-container">
<h1>Providers</h1>
<a href="add_provider.php" class="btn">Add Provider</a>
<?php
if (sset($_SESSION['authenticated'])){
echo "<br>";
echo "Welcome " . htmlspecialchars($_SESSION['name']);
}
?>
</div>
<input type="text" id="search" placeholder="Search providers...">
<table id="providers-table">
<thead>
<tr>
<th data-field="id">ID</th>
<th data-field="name">Name</th>
<th data-field="phone">Phone</th>
<th data-field="age">Age</th>
<th data-field="photo">Photo</th>
<th data-field="rates">Rates</th>
<th data-field="private">Private</th>
<th>Actions</th>
</tr>
</thead>
<tbody></tbody>
</table>
</div>
<script>
const providers = <?php echo json_encode($providers); ?>;
let currentSort = { field: null, asc: true };
// Helper to format rates into readable lines
function formatRates(rates) {
if (!rates) return '';
if (Array.isArray(rates)) {
return rates.map(r => {
const key = Object.keys(r)[0];
const val = r[key];
return `${key}: ${val}`;
}).join('<br>');
}
return rates;
}
function renderTable(data) {
const tbody = document.querySelector('#providers-table tbody');
tbody.innerHTML = '';
data.forEach(p => {
const tr = document.createElement('tr');
tr.innerHTML = `
<td>${p.id}</td>
<td>${p.name}</td>
<td>${p.phone || ''}</td>
<td>${p.age || ''}</td>
<td>${p.photo ? `<img src="${p.photo}" alt="Photo">` : ''}</td>
<td>${formatRates(p.rates)}</td>
<td>${p.private ? 'Yes' : 'No'}</td>
<td><a href="edit_provider.php?id=${p.id}">Edit</a></td>
`;
tbody.appendChild(tr);
});
}
function sortData(field) {
if (currentSort.field === field) {
currentSort.asc = !currentSort.asc;
} else {
currentSort.field = field;
currentSort.asc = true;
}
const sorted = providers.slice().sort((a, b) => {
let aVal = a[field];
let bVal = b[field];
if (['id', 'age', 'private'].includes(field)) {
aVal = Number(aVal);
bVal = Number(bVal);
} else {
aVal = (aVal || '').toString().toLowerCase();
bVal = (bVal || '').toString().toLowerCase();
}
if (aVal < bVal) return currentSort.asc ? -1 : 1;
if (aVal > bVal) return currentSort.asc ? 1 : -1;
return 0;
});
renderTable(sorted);
}
renderTable(providers);
document.querySelectorAll('#providers-table th').forEach(th => {
th.addEventListener('click', () => {
const field = th.getAttribute('data-field');
if (!field) return;
sortData(field);
});
});
document.getElementById('search').addEventListener('input', e => {
const query = e.target.value.toLowerCase();
const filtered = providers.filter(p => {
return Object.values(p).some(v => {
if (!v) return false;
return String(v).toLowerCase().includes(query);
});
});
renderTable(filtered);
});
</script>
</body>
</html>
+130
View File
@@ -0,0 +1,130 @@
<?php
// settings.php
// This page displays all settings stored in the Flask API and allows editing them.
// It supports updating existing settings via PUT and creating a single new setting via POST.
// The design follows the pattern used in list_providers.php.
// file settings
require_once __DIR__ . '/../fitness-config.php';
// --- Helper to call the Flask API -------------------------------------------
function callApi(string $method, string $path, $body = null)
{
// API base URL – update if your API is hosted elsewhere
$url = BASE_API_URL . $path;
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
if ($body !== null) {
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($body));
}
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($httpCode >= 400) {
return null; // Could also throw an exception if preferred
}
return json_decode($response, true);
}
$messages = [];
// Handle form submission – update existing settings and/or create a new one
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
// Update existing settings
if (isset($_POST['orig_name'], $_POST['value'])) {
foreach ($_POST['orig_name'] as $idx => $orig) {
$newVal = $_POST['value'][$idx] ?? '';
if ($newVal === '') continue; // skip empty values
$res = callApi('PUT', '/settings/' . urlencode($orig), ['value' => $newVal]);
if ($res === null) {
$messages[] = "Failed to update setting '" . htmlspecialchars($orig) . "'.";
} else {
$messages[] = "Updated setting '" . htmlspecialchars($orig) . "'.";
}
}
}
// Create a single new setting
$newName = $_POST['new_name'] ?? '';
$newValue = $_POST['new_value'] ?? '';
if ($newName !== '' && $newValue !== '') {
$res = callApi('POST', '/settings', ['name' => $newName, 'value' => $newValue]);
if ($res === null) {
$messages[] = "Failed to create new setting '" . htmlspecialchars($newName) . "'.";
} else {
$messages[] = "Created new setting '" . htmlspecialchars($newName) . "'.";
}
} elseif ($newName !== '' || $newValue !== '') {
$messages[] = "Both name and value are required to create a new setting.";
}
}
// Retrieve current settings to display
$settings = callApi('GET', '/settings');
$settings = $settings ?? [];
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Settings</title>
<link rel="stylesheet" href="https://cosmostat.matt-cloud.com/src/styles.css">
<style>
body {font-family: Arial, sans-serif; margin: 2rem;}
.messages {margin-bottom: 1rem;}
.messages .error {color: red;}
.messages .success {color: green;}
table {width: 100%; border-collapse: collapse;}
input[type=text] {width: 95%;}
.btn {padding: 6px 12px; margin-top: 10px;}
</style>
</head>
<body>
<div class="card">
<h1>Settings</h1>
<?php if (!empty($messages)) : ?>
<div class="messages">
<?php foreach ($messages as $msg) : ?>
<div class="success"><?= htmlspecialchars($msg) ?></div>
<?php endforeach; ?>
</div>
<?php endif; ?>
<form method="post">
<table>
<thead>
<tr>
<th>Name</th>
<th>Value</th>
</tr>
</thead>
<tbody>
<?php if (!empty($settings)) : ?>
<?php foreach ($settings as $s) : ?>
<tr>
<td><?= htmlspecialchars($s['name']) ?></td>
<td>
<input type="text" name="value[]" value="<?= htmlspecialchars($s['value']) ?>" />
<input type="hidden" name="orig_name[]" value="<?= htmlspecialchars($s['name']) ?>" />
</td>
</tr>
<?php endforeach; ?>
<?php endif; ?>
<tr>
<td><input type="text" name="new_name" placeholder="New setting name" /></td>
<td><input type="text" name="new_value" placeholder="New setting value" /></td>
</tr>
</tbody>
</table>
<input type="submit" value="Save Settings" class="btn" />
</form>
</div>
</body>
</html>