diff --git a/api/index.php b/api/index.php index dcb309a..987302f 100644 --- a/api/index.php +++ b/api/index.php @@ -920,6 +920,15 @@ try { case 'app_settings_save': appSettingsSave($db); break; + case 'locations_list': + locationsList($db); + break; + case 'locations_add': + locationsAdd($db); + break; + case 'locations_remove': + locationsRemove($db); + break; case 'recipes_list': recipesList($db); break; @@ -11949,6 +11958,81 @@ function familySiblingSuggest(PDO $db): void { ], JSON_UNESCAPED_UNICODE); } +// ===== CUSTOM LOCATIONS ===== + +function locationsList(PDO $db): void { + $rows = $db->query("SELECT key, label, icon, sort_order, is_builtin FROM locations ORDER BY sort_order ASC, id ASC")->fetchAll(); + echo json_encode(['success' => true, 'locations' => $rows]); +} + +function locationsAdd(PDO $db): void { + $input = json_decode(file_get_contents('php://input'), true) ?? []; + $label = trim($input['label'] ?? ''); + $icon = trim($input['icon'] ?? '📦'); + + if ($label === '') { + echo json_encode(['success' => false, 'error' => 'label required']); + return; + } + + // Generate a safe key from the label (slug-like, ASCII-only) + $key = mb_strtolower(trim($label)); + $key = preg_replace('/[^a-z0-9]+/u', '_', $key); + $key = trim($key, '_'); + if ($key === '') { + echo json_encode(['success' => false, 'error' => 'invalid label']); + return; + } + + $stmt = $db->prepare("SELECT id FROM locations WHERE key = ?"); + $stmt->execute([$key]); + if ($stmt->fetch()) { + echo json_encode(['success' => false, 'error' => 'location already exists']); + return; + } + + $maxOrder = (int)$db->query("SELECT COALESCE(MAX(sort_order), 0) FROM locations")->fetchColumn(); + + $stmt = $db->prepare("INSERT INTO locations (key, label, icon, sort_order, is_builtin) VALUES (?, ?, ?, ?, 0)"); + $stmt->execute([$key, $label, $icon, $maxOrder + 1]); + + echo json_encode(['success' => true, 'key' => $key]); +} + +function locationsRemove(PDO $db): void { + $input = json_decode(file_get_contents('php://input'), true) ?? []; + $key = trim($input['key'] ?? ''); + + if ($key === '') { + echo json_encode(['success' => false, 'error' => 'key required']); + return; + } + + $stmt = $db->prepare("SELECT is_builtin FROM locations WHERE key = ?"); + $stmt->execute([$key]); + $row = $stmt->fetch(); + + if (!$row) { + echo json_encode(['success' => false, 'error' => 'location not found']); + return; + } + if ((int)$row['is_builtin'] === 1) { + echo json_encode(['success' => false, 'error' => 'cannot delete a builtin location']); + return; + } + + // Guard: refuse deletion if inventory items still reference this location + $stmt = $db->prepare("SELECT COUNT(*) FROM inventory WHERE location = ? AND quantity > 0"); + $stmt->execute([$key]); + if ((int)$stmt->fetchColumn() > 0) { + echo json_encode(['success' => false, 'error' => 'location still has items in inventory']); + return; + } + + $db->prepare("DELETE FROM locations WHERE key = ?")->execute([$key]); + echo json_encode(['success' => true]); +} + // ===== SHARED APP DATA FUNCTIONS ===== function appSettingsGet(PDO $db): void {