- Introduced NFC support for chore submissions, allowing specific person credit after Head of Household approval. - Updated family settings to include NFC base URL, scan cooldown, and confirmation page options. - Enhanced chore management with options for anyone to complete chores and NFC link generation. - Improved API endpoints for handling NFC tokens and chore submissions. - Updated readme to reflect new NFC features and settings.
97 lines
3.0 KiB
PHP
97 lines
3.0 KiB
PHP
<?php
|
|
|
|
require_once __DIR__ . '/../includes/api_bootstrap.php';
|
|
|
|
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
|
sendJson(['success' => false, 'error' => 'Method not allowed'], 405);
|
|
}
|
|
|
|
$people = migrateAllPeople(normalizePeopleList(readJsonFile('people.json')));
|
|
assertHoHCanManagePeople($people);
|
|
|
|
$body = readJsonBody();
|
|
$id = isset($body['id']) ? trim((string) $body['id']) : '';
|
|
if ($id === '') {
|
|
sendJson(['success' => false, 'error' => 'id is required'], 400);
|
|
}
|
|
|
|
$idx = null;
|
|
foreach ($people as $i => $p) {
|
|
if (($p['id'] ?? '') === $id) {
|
|
$idx = $i;
|
|
break;
|
|
}
|
|
}
|
|
if ($idx === null) {
|
|
sendJson(['success' => false, 'error' => 'Person not found'], 404);
|
|
}
|
|
|
|
if (isset($body['name'])) {
|
|
$name = trim((string) $body['name']);
|
|
if ($name === '') {
|
|
sendJson(['success' => false, 'error' => 'Name cannot be empty'], 400);
|
|
}
|
|
$people[$idx]['name'] = $name;
|
|
}
|
|
|
|
if (array_key_exists('icon', $body)) {
|
|
$people[$idx]['icon'] = trim((string) $body['icon']);
|
|
}
|
|
if (array_key_exists('description', $body)) {
|
|
$people[$idx]['description'] = trim((string) $body['description']);
|
|
}
|
|
if (array_key_exists('birthday', $body)) {
|
|
$birthday = trim((string) $body['birthday']);
|
|
if ($birthday !== '' && !preg_match('/^\d{4}-\d{2}-\d{2}$/', $birthday)) {
|
|
sendJson(['success' => false, 'error' => 'birthday must be YYYY-MM-DD'], 400);
|
|
}
|
|
$people[$idx]['birthday'] = $birthday;
|
|
}
|
|
if (array_key_exists('favoriteColor', $body)) {
|
|
$c = trim((string) $body['favoriteColor']);
|
|
if ($c !== '' && !preg_match('/^#[0-9A-Fa-f]{6}$/', $c)) {
|
|
sendJson(['success' => false, 'error' => 'favoriteColor must be a #RRGGBB value'], 400);
|
|
}
|
|
if ($c !== '') {
|
|
$people[$idx]['favoriteColor'] = $c;
|
|
}
|
|
}
|
|
|
|
if (array_key_exists('role', $body)) {
|
|
$newRole = (string) $body['role'];
|
|
$allowedRoles = [ROLE_HEAD, ROLE_ADULT, ROLE_CHILD];
|
|
if (!in_array($newRole, $allowedRoles, true)) {
|
|
sendJson(['success' => false, 'error' => 'Invalid role'], 400);
|
|
}
|
|
$wasHead = ($people[$idx]['role'] ?? '') === ROLE_HEAD;
|
|
if ($newRole === ROLE_HEAD && !$wasHead) {
|
|
$pin = isset($body['pin']) ? (string) $body['pin'] : '';
|
|
if (strlen($pin) < 4) {
|
|
sendJson(['success' => false, 'error' => 'PIN must be at least 4 characters when promoting to Head of Household'], 400);
|
|
}
|
|
$people[$idx]['pin_hash'] = password_hash($pin, PASSWORD_DEFAULT);
|
|
}
|
|
if ($newRole !== ROLE_HEAD && $wasHead) {
|
|
$people[$idx]['pin_hash'] = null;
|
|
}
|
|
$people[$idx]['role'] = $newRole;
|
|
}
|
|
|
|
$headCount = 0;
|
|
foreach ($people as $p) {
|
|
if (($p['role'] ?? '') === ROLE_HEAD) {
|
|
$headCount++;
|
|
}
|
|
}
|
|
if ($headCount < 1) {
|
|
sendJson(['success' => false, 'error' => 'At least one Head of Household is required'], 400);
|
|
}
|
|
|
|
if (!writeJsonFile('people.json', $people)) {
|
|
sendJson(['success' => false, 'error' => 'Failed to save people'], 500);
|
|
}
|
|
|
|
$safe = $people[$idx];
|
|
unset($safe['pin_hash']);
|
|
sendJson(['success' => true, 'person' => $safe]);
|