Initial commit
This commit is contained in:
@@ -0,0 +1,88 @@
|
||||
<?php
|
||||
error_reporting(E_ALL);
|
||||
ini_set('display_errors', 0);
|
||||
header('Content-Type: application/json');
|
||||
|
||||
require_once 'config.php';
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
try {
|
||||
$data = json_decode(file_get_contents('php://input'), true);
|
||||
if (!is_array($data)) {
|
||||
$data = [];
|
||||
}
|
||||
|
||||
$db = getDB();
|
||||
|
||||
$households = [];
|
||||
if (isset($data['household_id']) && $data['household_id'] !== null && $data['household_id'] !== '') {
|
||||
if (!is_numeric($data['household_id'])) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'household_id must be numeric']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$stmt = $db->prepare('SELECT * FROM households WHERE id = ?');
|
||||
$stmt->execute([(int)$data['household_id']]);
|
||||
$household = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
if (!$household) {
|
||||
http_response_code(404);
|
||||
echo json_encode(['error' => 'Household not found']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$households[] = $household;
|
||||
} else {
|
||||
$stmt = $db->query('SELECT * FROM households ORDER BY id ASC');
|
||||
$households = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
}
|
||||
|
||||
if (empty($households)) {
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'message' => 'No households to score',
|
||||
'computed_count' => 0,
|
||||
'scores' => []
|
||||
]);
|
||||
exit;
|
||||
}
|
||||
|
||||
$scored = [];
|
||||
foreach ($households as $household) {
|
||||
$scoreData = computeHouseholdScores($db, $household);
|
||||
upsertHouseholdScore($db, $household['id'], $scoreData);
|
||||
|
||||
$scored[] = [
|
||||
'household_id' => (int)$household['id'],
|
||||
'household_code' => $household['household_code'],
|
||||
'head_name' => $household['head_name'],
|
||||
'skr' => $scoreData['skr'],
|
||||
'sag' => $scoreData['sag'],
|
||||
'spi' => $scoreData['spi'],
|
||||
'priority_level' => $scoreData['priority_level']
|
||||
];
|
||||
}
|
||||
|
||||
usort($scored, function ($a, $b) {
|
||||
if ($a['spi'] === $b['spi']) {
|
||||
return 0;
|
||||
}
|
||||
return ($a['spi'] < $b['spi']) ? 1 : -1;
|
||||
});
|
||||
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'computed_count' => count($scored),
|
||||
'scores' => $scored,
|
||||
'score_version' => 'v1'
|
||||
]);
|
||||
} catch (Exception $e) {
|
||||
http_response_code(500);
|
||||
echo json_encode(['error' => $e->getMessage()]);
|
||||
}
|
||||
} else {
|
||||
http_response_code(405);
|
||||
echo json_encode(['error' => 'Method not allowed']);
|
||||
}
|
||||
?>
|
||||
+1294
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
header('Content-Type: application/json');
|
||||
require_once 'config.php';
|
||||
|
||||
// Check authentication and authorization
|
||||
requireRole(['Admin']);
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||||
http_response_code(405);
|
||||
die(json_encode(['error' => 'Method not allowed']));
|
||||
}
|
||||
|
||||
$data = json_decode(file_get_contents('php://input'), true);
|
||||
|
||||
// Validate input
|
||||
if (!isset($data['username']) || !isset($data['email']) || !isset($data['password']) || !isset($data['role'])) {
|
||||
http_response_code(400);
|
||||
die(json_encode(['error' => 'Missing required fields: username, email, password, role']));
|
||||
}
|
||||
|
||||
$result = createUser($data['username'], $data['email'], $data['password'], $data['role']);
|
||||
|
||||
if ($result['success']) {
|
||||
http_response_code(201);
|
||||
} else {
|
||||
http_response_code(400);
|
||||
}
|
||||
|
||||
die(json_encode($result));
|
||||
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
error_reporting(E_ALL);
|
||||
ini_set('display_errors', 0);
|
||||
header('Content-Type: application/json');
|
||||
|
||||
require_once 'config.php';
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'DELETE') {
|
||||
try {
|
||||
$data = json_decode(file_get_contents('php://input'), true);
|
||||
|
||||
if (!is_array($data) || !isset($data['id'])) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'Missing household id']);
|
||||
exit;
|
||||
}
|
||||
|
||||
if (!is_numeric($data['id'])) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'id must be numeric']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$id = (int)$data['id'];
|
||||
$db = getDB();
|
||||
|
||||
$checkStmt = $db->prepare('SELECT id FROM households WHERE id = ?');
|
||||
$checkStmt->execute([$id]);
|
||||
if (!$checkStmt->fetch(PDO::FETCH_ASSOC)) {
|
||||
http_response_code(404);
|
||||
echo json_encode(['error' => 'Household not found']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$deleteStmt = $db->prepare('DELETE FROM households WHERE id = ?');
|
||||
$deleteStmt->execute([$id]);
|
||||
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'deleted_id' => $id
|
||||
]);
|
||||
} catch (Exception $e) {
|
||||
http_response_code(500);
|
||||
echo json_encode(['error' => $e->getMessage()]);
|
||||
}
|
||||
} else {
|
||||
http_response_code(405);
|
||||
echo json_encode(['error' => 'Method not allowed']);
|
||||
}
|
||||
?>
|
||||
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
error_reporting(E_ALL);
|
||||
ini_set('display_errors', 0);
|
||||
header('Content-Type: application/json');
|
||||
|
||||
require_once 'config.php';
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'DELETE') {
|
||||
try {
|
||||
$data = json_decode(file_get_contents('php://input'), true);
|
||||
|
||||
if (!isset($data['id'])) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'Missing road id']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$db = getDB();
|
||||
$stmt = $db->prepare('DELETE FROM roads WHERE id = ?');
|
||||
$stmt->execute([$data['id']]);
|
||||
|
||||
echo json_encode(['success' => true]);
|
||||
} catch (Exception $e) {
|
||||
http_response_code(500);
|
||||
echo json_encode(['error' => $e->getMessage()]);
|
||||
}
|
||||
} else {
|
||||
http_response_code(405);
|
||||
echo json_encode(['error' => 'Method not allowed']);
|
||||
}
|
||||
?>
|
||||
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
error_reporting(E_ALL);
|
||||
ini_set('display_errors', 0);
|
||||
header('Content-Type: application/json');
|
||||
|
||||
require_once 'config.php';
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'DELETE') {
|
||||
try {
|
||||
$data = json_decode(file_get_contents('php://input'), true);
|
||||
|
||||
if (!isset($data['id'])) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'Missing masjid id']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$id = (int)$data['id'];
|
||||
$db = getDB();
|
||||
|
||||
$checkStmt = $db->prepare('SELECT id FROM masjids WHERE id = ?');
|
||||
$checkStmt->execute([$id]);
|
||||
if (!$checkStmt->fetch(PDO::FETCH_ASSOC)) {
|
||||
http_response_code(404);
|
||||
echo json_encode(['error' => 'Masjid not found']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$childStmt = $db->prepare('SELECT id FROM need_points WHERE masjid_id = ?');
|
||||
$childStmt->execute([$id]);
|
||||
$childIds = array_map(function($row) {
|
||||
return (int)$row['id'];
|
||||
}, $childStmt->fetchAll(PDO::FETCH_ASSOC));
|
||||
|
||||
$deleteStmt = $db->prepare('DELETE FROM masjids WHERE id = ?');
|
||||
$deleteStmt->execute([$id]);
|
||||
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'deleted_need_points' => count($childIds),
|
||||
'deleted_need_point_ids' => $childIds
|
||||
]);
|
||||
} catch (Exception $e) {
|
||||
http_response_code(500);
|
||||
echo json_encode(['error' => $e->getMessage()]);
|
||||
}
|
||||
} else {
|
||||
http_response_code(405);
|
||||
echo json_encode(['error' => 'Method not allowed']);
|
||||
}
|
||||
?>
|
||||
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
error_reporting(E_ALL);
|
||||
ini_set('display_errors', 0);
|
||||
header('Content-Type: application/json');
|
||||
|
||||
require_once 'config.php';
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'DELETE') {
|
||||
try {
|
||||
$data = json_decode(file_get_contents('php://input'), true);
|
||||
|
||||
if (!isset($data['id'])) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'Missing point id']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$id = (int)$data['id'];
|
||||
$db = getDB();
|
||||
|
||||
$deleteStmt = $db->prepare('DELETE FROM need_points WHERE id = ?');
|
||||
$deleteStmt->execute([$id]);
|
||||
|
||||
if ($deleteStmt->rowCount() === 0) {
|
||||
http_response_code(404);
|
||||
echo json_encode(['error' => 'Point berkebutuhan not found']);
|
||||
exit;
|
||||
}
|
||||
|
||||
echo json_encode(['success' => true]);
|
||||
} catch (Exception $e) {
|
||||
http_response_code(500);
|
||||
echo json_encode(['error' => $e->getMessage()]);
|
||||
}
|
||||
} else {
|
||||
http_response_code(405);
|
||||
echo json_encode(['error' => 'Method not allowed']);
|
||||
}
|
||||
?>
|
||||
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
error_reporting(E_ALL);
|
||||
ini_set('display_errors', 0);
|
||||
header('Content-Type: application/json');
|
||||
|
||||
require_once 'config.php';
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'DELETE') {
|
||||
try {
|
||||
$data = json_decode(file_get_contents('php://input'), true);
|
||||
|
||||
if (!isset($data['id'])) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'Missing marker id']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$db = getDB();
|
||||
$stmt = $db->prepare('DELETE FROM markers WHERE id = ?');
|
||||
$stmt->execute([$data['id']]);
|
||||
|
||||
echo json_encode(['success' => true]);
|
||||
} catch (Exception $e) {
|
||||
http_response_code(500);
|
||||
echo json_encode(['error' => $e->getMessage()]);
|
||||
}
|
||||
} else {
|
||||
http_response_code(405);
|
||||
echo json_encode(['error' => 'Method not allowed']);
|
||||
}
|
||||
?>
|
||||
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
error_reporting(E_ALL);
|
||||
ini_set('display_errors', 0);
|
||||
header('Content-Type: application/json');
|
||||
|
||||
require_once 'config.php';
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'DELETE') {
|
||||
try {
|
||||
$data = json_decode(file_get_contents('php://input'), true);
|
||||
|
||||
if (!isset($data['id'])) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'Missing parcel id']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$db = getDB();
|
||||
$stmt = $db->prepare('DELETE FROM land_parcels WHERE id = ?');
|
||||
$stmt->execute([$data['id']]);
|
||||
|
||||
echo json_encode(['success' => true]);
|
||||
} catch (Exception $e) {
|
||||
http_response_code(500);
|
||||
echo json_encode(['error' => $e->getMessage()]);
|
||||
}
|
||||
} else {
|
||||
http_response_code(405);
|
||||
echo json_encode(['error' => 'Method not allowed']);
|
||||
}
|
||||
?>
|
||||
@@ -0,0 +1,58 @@
|
||||
<?php
|
||||
header('Content-Type: application/json');
|
||||
require_once 'config.php';
|
||||
|
||||
// Check authentication and authorization
|
||||
requireRole(['Admin']);
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'DELETE') {
|
||||
http_response_code(405);
|
||||
die(json_encode(['error' => 'Method not allowed']));
|
||||
}
|
||||
|
||||
$data = json_decode(file_get_contents('php://input'), true);
|
||||
|
||||
// Validate input
|
||||
if (!isset($data['id'])) {
|
||||
http_response_code(400);
|
||||
die(json_encode(['error' => 'Missing required field: id']));
|
||||
}
|
||||
|
||||
try {
|
||||
$db = getDB();
|
||||
|
||||
// Prevent deactivating the only admin
|
||||
$adminCount = $db->query('SELECT COUNT(*) as cnt FROM users WHERE role = \'Admin\' AND is_active = 1')->fetch(PDO::FETCH_ASSOC)['cnt'];
|
||||
|
||||
$stmt = $db->prepare('SELECT role FROM users WHERE id = ?');
|
||||
$stmt->execute([$data['id']]);
|
||||
$user = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
if (!$user) {
|
||||
http_response_code(404);
|
||||
die(json_encode(['error' => 'User not found']));
|
||||
}
|
||||
|
||||
if ($user['role'] === 'Admin' && $adminCount <= 1) {
|
||||
http_response_code(400);
|
||||
die(json_encode(['error' => 'Cannot deactivate the only admin user']));
|
||||
}
|
||||
|
||||
// Deactivate user (soft delete)
|
||||
$updateStmt = $db->prepare('
|
||||
UPDATE users
|
||||
SET is_active = 0, updated_at = CURRENT_TIMESTAMP
|
||||
WHERE id = ?
|
||||
');
|
||||
$updateStmt->execute([$data['id']]);
|
||||
|
||||
http_response_code(200);
|
||||
die(json_encode([
|
||||
'success' => true,
|
||||
'message' => 'User deactivated successfully'
|
||||
]));
|
||||
|
||||
} catch (PDOException $e) {
|
||||
http_response_code(500);
|
||||
die(json_encode(['error' => 'Database error: ' . $e->getMessage()]));
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
<?php
|
||||
header('Content-Type: application/geo+json');
|
||||
header('Content-Disposition: attachment; filename="export.geojson"');
|
||||
require_once 'config.php';
|
||||
|
||||
try {
|
||||
$db = getDB();
|
||||
|
||||
// Get type parameter
|
||||
$type = isset($_GET['type']) ? $_GET['type'] : 'roads';
|
||||
|
||||
// Validate type
|
||||
$valid_types = ['roads', 'parcels', 'pontianak_areas'];
|
||||
if (!in_array($type, $valid_types)) {
|
||||
throw new Exception('Invalid type. Must be: ' . implode(', ', $valid_types));
|
||||
}
|
||||
|
||||
$features = [];
|
||||
|
||||
if ($type === 'roads') {
|
||||
// Export roads as LineString features
|
||||
$stmt = $db->prepare('SELECT id, name, road_type, length_meters, condition_status, coordinates, created_at FROM roads');
|
||||
$stmt->execute();
|
||||
$roads = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
foreach ($roads as $road) {
|
||||
$coordinates = json_decode($road['coordinates'], true);
|
||||
|
||||
// Convert [lat, lng] to [lng, lat] for GeoJSON
|
||||
$geojson_coords = [];
|
||||
foreach ($coordinates as $coord) {
|
||||
$geojson_coords[] = [$coord[1], $coord[0]];
|
||||
}
|
||||
|
||||
$features[] = [
|
||||
'type' => 'Feature',
|
||||
'geometry' => [
|
||||
'type' => 'LineString',
|
||||
'coordinates' => $geojson_coords
|
||||
],
|
||||
'properties' => [
|
||||
'id' => $road['id'],
|
||||
'name' => $road['name'],
|
||||
'road_type' => $road['road_type'],
|
||||
'length_meters' => floatval($road['length_meters']),
|
||||
'length_km' => floatval($road['length_meters']) / 1000,
|
||||
'condition_status' => $road['condition_status'],
|
||||
'created_at' => $road['created_at']
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
} else if ($type === 'parcels') {
|
||||
// Export parcels as Polygon features
|
||||
$stmt = $db->prepare('SELECT id, name, certificate_type, area_sqm, coordinates, created_at FROM land_parcels');
|
||||
$stmt->execute();
|
||||
$parcels = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
foreach ($parcels as $parcel) {
|
||||
$coordinates = json_decode($parcel['coordinates'], true);
|
||||
|
||||
// Convert [lat, lng] to [lng, lat] for GeoJSON
|
||||
$geojson_coords = [];
|
||||
foreach ($coordinates as $coord) {
|
||||
$geojson_coords[] = [$coord[1], $coord[0]];
|
||||
}
|
||||
// Close the polygon
|
||||
if ($geojson_coords[0] !== end($geojson_coords)) {
|
||||
$geojson_coords[] = $geojson_coords[0];
|
||||
}
|
||||
|
||||
$features[] = [
|
||||
'type' => 'Feature',
|
||||
'geometry' => [
|
||||
'type' => 'Polygon',
|
||||
'coordinates' => [$geojson_coords]
|
||||
],
|
||||
'properties' => [
|
||||
'id' => $parcel['id'],
|
||||
'name' => $parcel['name'],
|
||||
'certificate_type' => $parcel['certificate_type'],
|
||||
'area_sqm' => floatval($parcel['area_sqm']),
|
||||
'area_hectares' => floatval($parcel['area_sqm']) / 10000,
|
||||
'created_at' => $parcel['created_at']
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
} else if ($type === 'pontianak_areas') {
|
||||
// Export Pontianak areas as features
|
||||
$stmt = $db->prepare('SELECT id, name, area_type, geometry, population FROM pontianak_areas ORDER BY area_type, name');
|
||||
$stmt->execute();
|
||||
$areas = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
foreach ($areas as $area) {
|
||||
$geometry = json_decode($area['geometry'], true);
|
||||
|
||||
$features[] = [
|
||||
'type' => 'Feature',
|
||||
'geometry' => $geometry,
|
||||
'properties' => [
|
||||
'id' => $area['id'],
|
||||
'name' => $area['name'],
|
||||
'area_type' => $area['area_type'],
|
||||
'population' => intval($area['population'])
|
||||
]
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
// Build FeatureCollection
|
||||
$geojson = [
|
||||
'type' => 'FeatureCollection',
|
||||
'features' => $features
|
||||
];
|
||||
|
||||
// Output GeoJSON
|
||||
echo json_encode($geojson, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
|
||||
|
||||
} catch (PDOException $e) {
|
||||
http_response_code(500);
|
||||
echo json_encode([
|
||||
'success' => false,
|
||||
'error' => 'Database error: ' . $e->getMessage()
|
||||
]);
|
||||
} catch (Exception $e) {
|
||||
http_response_code(400);
|
||||
echo json_encode([
|
||||
'success' => false,
|
||||
'error' => $e->getMessage()
|
||||
]);
|
||||
}
|
||||
?>
|
||||
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
// Backfill missing household fields for existing need_points
|
||||
header('Content-Type: application/json');
|
||||
require_once 'config.php';
|
||||
|
||||
try {
|
||||
$db = getDB();
|
||||
|
||||
// Fetch points missing any of the new fields
|
||||
$stmt = $db->query("SELECT id, name FROM need_points WHERE household_name IS NULL OR TRIM(household_name) = '' OR head_name IS NULL OR TRIM(head_name) = '' OR economic_status IS NULL OR TRIM(economic_status) = ''");
|
||||
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
if (empty($rows)) {
|
||||
echo json_encode(['success' => true, 'updated' => 0, 'message' => 'No need_points required backfill.']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$updateStmt = $db->prepare('UPDATE need_points SET household_name = ?, head_name = ?, economic_status = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?');
|
||||
|
||||
$firstNames = ['Ahmad','Budi','Siti','Aulia','Rizal','Dewi','Agus','Fitri','Hendra','Yulia'];
|
||||
$lastNames = ['Santoso','Wijaya','Pratama','Hidayat','Saputra','Nur','Ibrahim','Setiawan','Rahman','Putri'];
|
||||
$statuses = ['sangat_miskin','miskin','rentan','cukup','baik'];
|
||||
|
||||
$db->beginTransaction();
|
||||
$updated = 0;
|
||||
$examples = [];
|
||||
foreach ($rows as $r) {
|
||||
$id = (int)$r['id'];
|
||||
$hhName = 'Keluarga ' . substr(md5($r['name'] . $id . microtime(true)), 0, 8);
|
||||
$headName = $firstNames[array_rand($firstNames)] . ' ' . $lastNames[array_rand($lastNames)];
|
||||
$econ = $statuses[array_rand($statuses)];
|
||||
|
||||
$updateStmt->execute([$hhName, $headName, $econ, $id]);
|
||||
$updated++;
|
||||
if (count($examples) < 8) {
|
||||
$examples[] = ['id' => $id, 'household_name' => $hhName, 'head_name' => $headName, 'economic_status' => $econ];
|
||||
}
|
||||
}
|
||||
$db->commit();
|
||||
|
||||
echo json_encode(['success' => true, 'updated' => $updated, 'examples' => $examples]);
|
||||
} catch (Exception $e) {
|
||||
if (isset($db) && $db->inTransaction()) $db->rollBack();
|
||||
http_response_code(500);
|
||||
echo json_encode(['success' => false, 'error' => $e->getMessage()]);
|
||||
}
|
||||
|
||||
// If called from CLI, also print a newline
|
||||
if (php_sapi_name() === 'cli') echo "\n";
|
||||
|
||||
?>
|
||||
@@ -0,0 +1,79 @@
|
||||
<?php
|
||||
header('Content-Type: application/json');
|
||||
require_once 'config.php';
|
||||
|
||||
try {
|
||||
$db = getDB();
|
||||
|
||||
// Derive province totals from seeded need-point names.
|
||||
// The peta seed data stores the region name inside need_points.name, so we
|
||||
// map those region labels back to provinces and rank them by count.
|
||||
$provinceMap = [
|
||||
'DKI Jakarta' => ['Jakarta'],
|
||||
'Jawa Barat' => ['Bandung', 'Bekasi'],
|
||||
'Jawa Tengah' => ['Semarang'],
|
||||
'DI Yogyakarta' => ['Yogyakarta'],
|
||||
'Jawa Timur' => ['Surabaya', 'Malang'],
|
||||
'Sumatera Utara' => ['Medan'],
|
||||
'Sumatera Selatan' => ['Palembang'],
|
||||
'Riau' => ['Pekanbaru'],
|
||||
'Jambi' => ['Jambi'],
|
||||
'Sumatera Barat' => ['Padang'],
|
||||
'Sulawesi Selatan' => ['Makassar'],
|
||||
'Sulawesi Utara' => ['Manado'],
|
||||
'Sulawesi Tenggara' => ['Kendari'],
|
||||
'Sulawesi Tengah' => ['Palu'],
|
||||
'Kalimantan Selatan' => ['Banjarmasin'],
|
||||
'Kalimantan Barat' => ['Pontianak'],
|
||||
'Kalimantan Timur' => ['Samarinda'],
|
||||
'Kalimantan Tengah' => ['Palangkaraya'],
|
||||
'Papua' => ['Jayapura'],
|
||||
'Nusa Tenggara Barat' => ['Mataram'],
|
||||
'Nusa Tenggara Timur' => ['Kupang'],
|
||||
'Maluku' => ['Ambon'],
|
||||
'Bali' => ['Denpasar']
|
||||
];
|
||||
|
||||
$needPoints = $db->query('SELECT name FROM need_points')->fetchAll(PDO::FETCH_COLUMN);
|
||||
$provinceCounts = [];
|
||||
|
||||
foreach ($provinceMap as $province => $regions) {
|
||||
$provinceCounts[$province] = 0;
|
||||
foreach ($needPoints as $pointName) {
|
||||
foreach ($regions as $region) {
|
||||
if (stripos($pointName, $region) !== false) {
|
||||
$provinceCounts[$province]++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
arsort($provinceCounts);
|
||||
$topProvinces = array_slice($provinceCounts, 0, 5, true);
|
||||
|
||||
$areaStats = [];
|
||||
foreach ($topProvinces as $provinceName => $count) {
|
||||
$areaStats[] = [
|
||||
'id' => null,
|
||||
'area_name' => $provinceName,
|
||||
'area_type' => 'province',
|
||||
'household_count' => (int)$count,
|
||||
'avg_poverty_score' => 0,
|
||||
'verified_count' => 0,
|
||||
'assisted_count' => 0
|
||||
];
|
||||
}
|
||||
|
||||
$response = [
|
||||
'success' => true,
|
||||
'areas' => $areaStats
|
||||
];
|
||||
|
||||
http_response_code(200);
|
||||
die(json_encode($response));
|
||||
|
||||
} catch (PDOException $e) {
|
||||
http_response_code(500);
|
||||
die(json_encode(['error' => 'Database error: ' . $e->getMessage()]));
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
<?php
|
||||
error_reporting(E_ALL);
|
||||
ini_set('display_errors', 0);
|
||||
header('Content-Type: application/json');
|
||||
|
||||
require_once 'config.php';
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'GET') {
|
||||
try {
|
||||
$db = getDB();
|
||||
|
||||
$sql = '
|
||||
SELECT
|
||||
ad.*,
|
||||
h.household_code,
|
||||
h.head_name,
|
||||
h.verification_status AS household_verification_status
|
||||
FROM assistance_distributions ad
|
||||
JOIN households h ON h.id = ad.household_id
|
||||
WHERE 1 = 1
|
||||
';
|
||||
$params = [];
|
||||
|
||||
if (isset($_GET['household_id']) && $_GET['household_id'] !== '') {
|
||||
if (!is_numeric($_GET['household_id'])) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'household_id must be numeric']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$sql .= ' AND ad.household_id = ?';
|
||||
$params[] = (int)$_GET['household_id'];
|
||||
}
|
||||
|
||||
if (isset($_GET['assistance_type']) && $_GET['assistance_type'] !== '') {
|
||||
$error = null;
|
||||
$assistanceType = normalizeAssistanceType($_GET['assistance_type'], $error);
|
||||
if ($assistanceType === null) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => $error]);
|
||||
exit;
|
||||
}
|
||||
|
||||
$sql .= ' AND ad.assistance_type = ?';
|
||||
$params[] = $assistanceType;
|
||||
}
|
||||
|
||||
if (isset($_GET['delivery_status']) && $_GET['delivery_status'] !== '') {
|
||||
$error = null;
|
||||
$deliveryStatus = normalizeDeliveryStatus($_GET['delivery_status'], $error);
|
||||
if ($deliveryStatus === null) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => $error]);
|
||||
exit;
|
||||
}
|
||||
|
||||
$sql .= ' AND ad.delivery_status = ?';
|
||||
$params[] = $deliveryStatus;
|
||||
}
|
||||
|
||||
if (isset($_GET['date_from']) && $_GET['date_from'] !== '') {
|
||||
$dateFrom = trim((string)$_GET['date_from']);
|
||||
$dateObj = DateTime::createFromFormat('Y-m-d', $dateFrom);
|
||||
if (!$dateObj || $dateObj->format('Y-m-d') !== $dateFrom) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'date_from must be in YYYY-MM-DD format']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$sql .= ' AND ad.distribution_date >= ?';
|
||||
$params[] = $dateFrom;
|
||||
}
|
||||
|
||||
if (isset($_GET['date_to']) && $_GET['date_to'] !== '') {
|
||||
$dateTo = trim((string)$_GET['date_to']);
|
||||
$dateObj = DateTime::createFromFormat('Y-m-d', $dateTo);
|
||||
if (!$dateObj || $dateObj->format('Y-m-d') !== $dateTo) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'date_to must be in YYYY-MM-DD format']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$sql .= ' AND ad.distribution_date <= ?';
|
||||
$params[] = $dateTo;
|
||||
}
|
||||
|
||||
$limit = 200;
|
||||
if (isset($_GET['limit']) && $_GET['limit'] !== '') {
|
||||
if (!is_numeric($_GET['limit'])) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'limit must be numeric']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$limit = max(1, min((int)$_GET['limit'], 1000));
|
||||
}
|
||||
|
||||
$sql .= ' ORDER BY ad.distribution_date DESC, ad.created_at DESC LIMIT ' . $limit;
|
||||
|
||||
$stmt = $db->prepare($sql);
|
||||
$stmt->execute($params);
|
||||
$distributions = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
$summarySql = '
|
||||
SELECT
|
||||
COUNT(*) AS total_records,
|
||||
COALESCE(SUM(assistance_value), 0) AS total_assistance_value,
|
||||
COALESCE(SUM(CASE WHEN delivery_status = \'delivered\' THEN assistance_value ELSE 0 END), 0) AS delivered_assistance_value
|
||||
FROM assistance_distributions
|
||||
';
|
||||
$summaryStmt = $db->query($summarySql);
|
||||
$summary = $summaryStmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'count' => count($distributions),
|
||||
'distributions' => $distributions,
|
||||
'summary' => $summary
|
||||
]);
|
||||
} catch (Exception $e) {
|
||||
http_response_code(500);
|
||||
echo json_encode(['error' => $e->getMessage()]);
|
||||
}
|
||||
} else {
|
||||
http_response_code(405);
|
||||
echo json_encode(['error' => 'Method not allowed']);
|
||||
}
|
||||
?>
|
||||
@@ -0,0 +1,479 @@
|
||||
<?php
|
||||
header('Content-Type: application/json');
|
||||
require_once 'config.php';
|
||||
|
||||
function decodeGeometry($rawGeometry) {
|
||||
if (is_array($rawGeometry)) {
|
||||
return $rawGeometry;
|
||||
}
|
||||
$decoded = json_decode($rawGeometry, true);
|
||||
return is_array($decoded) ? $decoded : null;
|
||||
}
|
||||
|
||||
function geometryRings($geometry) {
|
||||
if (!is_array($geometry) || !isset($geometry['type']) || !isset($geometry['coordinates'])) {
|
||||
return [];
|
||||
}
|
||||
|
||||
if ($geometry['type'] === 'Polygon') {
|
||||
return [$geometry['coordinates']];
|
||||
}
|
||||
|
||||
if ($geometry['type'] === 'MultiPolygon') {
|
||||
return $geometry['coordinates'];
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
function projectLonLatToMeters($lng, $lat, $referenceLat) {
|
||||
$earthRadius = 6371000.0;
|
||||
$x = deg2rad((float)$lng) * $earthRadius * cos(deg2rad((float)$referenceLat));
|
||||
$y = deg2rad((float)$lat) * $earthRadius;
|
||||
return [$x, $y];
|
||||
}
|
||||
|
||||
function ringAreaSquareMeters($ring) {
|
||||
if (!is_array($ring) || count($ring) < 4) {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
$sumLat = 0.0;
|
||||
$count = 0;
|
||||
foreach ($ring as $point) {
|
||||
if (is_array($point) && count($point) >= 2) {
|
||||
$sumLat += (float)$point[1];
|
||||
$count++;
|
||||
}
|
||||
}
|
||||
|
||||
if ($count === 0) {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
$referenceLat = $sumLat / $count;
|
||||
$projected = [];
|
||||
foreach ($ring as $point) {
|
||||
if (is_array($point) && count($point) >= 2) {
|
||||
$projected[] = projectLonLatToMeters((float)$point[0], (float)$point[1], $referenceLat);
|
||||
}
|
||||
}
|
||||
|
||||
if (count($projected) < 4) {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
$area = 0.0;
|
||||
$maxIndex = count($projected) - 1;
|
||||
for ($i = 0; $i < $maxIndex; $i++) {
|
||||
$area += ($projected[$i][0] * $projected[$i + 1][1]) - ($projected[$i + 1][0] * $projected[$i][1]);
|
||||
}
|
||||
|
||||
return abs($area / 2.0);
|
||||
}
|
||||
|
||||
function geometryAreaKm2($geometry) {
|
||||
$polygons = geometryRings($geometry);
|
||||
if (empty($polygons)) {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
$totalSquareMeters = 0.0;
|
||||
|
||||
foreach ($polygons as $polygonRings) {
|
||||
if (!is_array($polygonRings) || empty($polygonRings)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$outerArea = ringAreaSquareMeters($polygonRings[0]);
|
||||
$holeArea = 0.0;
|
||||
|
||||
for ($i = 1; $i < count($polygonRings); $i++) {
|
||||
$holeArea += ringAreaSquareMeters($polygonRings[$i]);
|
||||
}
|
||||
|
||||
$totalSquareMeters += max($outerArea - $holeArea, 0.0);
|
||||
}
|
||||
|
||||
return $totalSquareMeters / 1000000.0;
|
||||
}
|
||||
|
||||
function pointInRing($lng, $lat, $ring) {
|
||||
if (!is_array($ring) || count($ring) < 4) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$inside = false;
|
||||
$lastIndex = count($ring) - 1;
|
||||
|
||||
for ($i = 0, $j = $lastIndex; $i <= $lastIndex; $j = $i++) {
|
||||
$xi = (float)$ring[$i][0];
|
||||
$yi = (float)$ring[$i][1];
|
||||
$xj = (float)$ring[$j][0];
|
||||
$yj = (float)$ring[$j][1];
|
||||
|
||||
$intersect = (($yi > $lat) !== ($yj > $lat)) &&
|
||||
($lng < ($xj - $xi) * ($lat - $yi) / (($yj - $yi) ?: 1e-12) + $xi);
|
||||
|
||||
if ($intersect) {
|
||||
$inside = !$inside;
|
||||
}
|
||||
}
|
||||
|
||||
return $inside;
|
||||
}
|
||||
|
||||
function pointInGeometry($lng, $lat, $geometry) {
|
||||
$polygons = geometryRings($geometry);
|
||||
if (empty($polygons)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach ($polygons as $polygonRings) {
|
||||
if (!is_array($polygonRings) || empty($polygonRings)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!pointInRing($lng, $lat, $polygonRings[0])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$insideHole = false;
|
||||
for ($i = 1; $i < count($polygonRings); $i++) {
|
||||
if (pointInRing($lng, $lat, $polygonRings[$i])) {
|
||||
$insideHole = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$insideHole) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function geometryCentroid($geometry) {
|
||||
$polygons = geometryRings($geometry);
|
||||
$sumLng = 0.0;
|
||||
$sumLat = 0.0;
|
||||
$count = 0;
|
||||
|
||||
foreach ($polygons as $polygonRings) {
|
||||
if (!is_array($polygonRings) || empty($polygonRings) || !is_array($polygonRings[0])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach ($polygonRings[0] as $point) {
|
||||
if (is_array($point) && count($point) >= 2) {
|
||||
$sumLng += (float)$point[0];
|
||||
$sumLat += (float)$point[1];
|
||||
$count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($count === 0) {
|
||||
return ['lng' => 109.33, 'lat' => -0.02];
|
||||
}
|
||||
|
||||
return [
|
||||
'lng' => $sumLng / $count,
|
||||
'lat' => $sumLat / $count
|
||||
];
|
||||
}
|
||||
|
||||
function haversineMeters($lat1, $lng1, $lat2, $lng2) {
|
||||
$earthRadius = 6371000.0;
|
||||
$dLat = deg2rad((float)$lat2 - (float)$lat1);
|
||||
$dLng = deg2rad((float)$lng2 - (float)$lng1);
|
||||
|
||||
$a = sin($dLat / 2) * sin($dLat / 2)
|
||||
+ cos(deg2rad((float)$lat1)) * cos(deg2rad((float)$lat2))
|
||||
* sin($dLng / 2) * sin($dLng / 2);
|
||||
|
||||
$c = 2 * atan2(sqrt($a), sqrt(max(1 - $a, 0.0)));
|
||||
return $earthRadius * $c;
|
||||
}
|
||||
|
||||
function roadCentroidLngLat($coordinates) {
|
||||
if (!is_array($coordinates) || empty($coordinates)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$sumLat = 0.0;
|
||||
$sumLng = 0.0;
|
||||
$count = 0;
|
||||
|
||||
foreach ($coordinates as $pair) {
|
||||
if (is_array($pair) && count($pair) >= 2 && is_numeric($pair[0]) && is_numeric($pair[1])) {
|
||||
// Road coordinates are stored as [lat, lng]
|
||||
$sumLat += (float)$pair[0];
|
||||
$sumLng += (float)$pair[1];
|
||||
$count++;
|
||||
}
|
||||
}
|
||||
|
||||
if ($count === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return [
|
||||
'lng' => $sumLng / $count,
|
||||
'lat' => $sumLat / $count
|
||||
];
|
||||
}
|
||||
|
||||
function deterministicRoadDensity($populationDensity, $areaId) {
|
||||
$seed = ($areaId * 1103515245 + 12345) & 0x7fffffff;
|
||||
$variation = 0.8 + (($seed % 1000) / 1000.0) * 0.6;
|
||||
$base = 0.9 + (log(max($populationDensity, 1.0) + 1.0) / log(10)) * 1.4;
|
||||
|
||||
return round($base * $variation, 6);
|
||||
}
|
||||
|
||||
function deterministicDamagedDensity($roadDensity, $areaId) {
|
||||
$seed = ($areaId * 214013 + 2531011) & 0x7fffffff;
|
||||
$ratio = 0.14 + (($seed % 1000) / 1000.0) * 0.18;
|
||||
return round($roadDensity * $ratio, 6);
|
||||
}
|
||||
|
||||
try {
|
||||
$db = getDB();
|
||||
|
||||
// Get layer type parameter
|
||||
$layer_type = isset($_GET['layer_type']) ? $_GET['layer_type'] : 'population';
|
||||
$admin_level = isset($_GET['admin_level']) ? strtolower($_GET['admin_level']) : 'kecamatan';
|
||||
|
||||
// Validate layer type
|
||||
$valid_layers = ['population', 'road_density', 'damaged_roads_density', 'city_boundary'];
|
||||
if (!in_array($layer_type, $valid_layers)) {
|
||||
throw new Exception('Invalid layer type. Must be: ' . implode(', ', $valid_layers));
|
||||
}
|
||||
|
||||
// Special handling for city boundary
|
||||
if ($layer_type === 'city_boundary') {
|
||||
$stmt = $db->prepare('SELECT id, name, geometry FROM pontianak_areas WHERE area_type = ?');
|
||||
$stmt->execute(['city']);
|
||||
$cities = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
$data_items = [];
|
||||
foreach ($cities as $city) {
|
||||
$data_items[] = [
|
||||
'area_id' => $city['id'],
|
||||
'area_name' => $city['name'],
|
||||
'value' => 0, // No numeric value for boundary
|
||||
'geometry' => json_decode($city['geometry'], true)
|
||||
];
|
||||
}
|
||||
|
||||
http_response_code(200);
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'layer_type' => 'city_boundary',
|
||||
'data' => $data_items,
|
||||
'min_value' => 0.0,
|
||||
'max_value' => 1.0,
|
||||
'admin_level' => 'city',
|
||||
'value_source' => 'boundary'
|
||||
]);
|
||||
exit;
|
||||
}
|
||||
|
||||
$valid_admin_levels = ['kecamatan', 'kelurahan'];
|
||||
if (!in_array($admin_level, $valid_admin_levels)) {
|
||||
throw new Exception('Invalid admin_level. Must be: ' . implode(', ', $valid_admin_levels));
|
||||
}
|
||||
|
||||
// Get Pontianak areas by selected admin level
|
||||
$stmt = $db->prepare('SELECT id, name, geometry, population FROM pontianak_areas WHERE area_type = ? ORDER BY name');
|
||||
$stmt->execute([$admin_level]);
|
||||
$areas = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
if (empty($areas)) {
|
||||
// Fallback to kecamatan when kelurahan data does not exist.
|
||||
if ($admin_level === 'kelurahan') {
|
||||
$stmt->execute(['kecamatan']);
|
||||
$areas = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
$admin_level = 'kecamatan';
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($areas)) {
|
||||
throw new Exception('No Pontianak areas found for selected admin level. Run seed_pontianak_data.php?force=true first.');
|
||||
}
|
||||
|
||||
$areaMetrics = [];
|
||||
foreach ($areas as $area) {
|
||||
$geometry = decodeGeometry($area['geometry']);
|
||||
if ($geometry === null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$areaKm2 = geometryAreaKm2($geometry);
|
||||
if ($areaKm2 <= 0) {
|
||||
$areaKm2 = 0.01;
|
||||
}
|
||||
|
||||
$population = (float)$area['population'];
|
||||
$populationDensity = $population / $areaKm2;
|
||||
|
||||
$areaMetrics[(int)$area['id']] = [
|
||||
'id' => (int)$area['id'],
|
||||
'name' => $area['name'],
|
||||
'geometry' => $geometry,
|
||||
'area_km2' => $areaKm2,
|
||||
'centroid' => geometryCentroid($geometry),
|
||||
'population' => $population,
|
||||
'population_density' => $populationDensity
|
||||
];
|
||||
}
|
||||
|
||||
if (empty($areaMetrics)) {
|
||||
throw new Exception('Area geometry data is invalid for selected admin level.');
|
||||
}
|
||||
|
||||
$areaRoadCounts = [];
|
||||
$areaDamagedRoadCounts = [];
|
||||
foreach ($areaMetrics as $metric) {
|
||||
$areaRoadCounts[$metric['id']] = 0;
|
||||
$areaDamagedRoadCounts[$metric['id']] = 0;
|
||||
}
|
||||
|
||||
$roadsExist = false;
|
||||
$valueSource = 'computed';
|
||||
|
||||
if ($layer_type === 'road_density' || $layer_type === 'damaged_roads_density') {
|
||||
$roadStmt = $db->query('SELECT id, coordinates, condition_status FROM roads');
|
||||
$roads = $roadStmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
foreach ($roads as $road) {
|
||||
$coordinates = json_decode($road['coordinates'], true);
|
||||
$centroid = roadCentroidLngLat($coordinates);
|
||||
if ($centroid === null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$roadsExist = true;
|
||||
|
||||
$assignedAreaId = null;
|
||||
foreach ($areaMetrics as $areaMetric) {
|
||||
if (pointInGeometry($centroid['lng'], $centroid['lat'], $areaMetric['geometry'])) {
|
||||
$assignedAreaId = $areaMetric['id'];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ($assignedAreaId === null) {
|
||||
$nearestDistance = null;
|
||||
foreach ($areaMetrics as $areaMetric) {
|
||||
$distance = haversineMeters(
|
||||
$centroid['lat'],
|
||||
$centroid['lng'],
|
||||
$areaMetric['centroid']['lat'],
|
||||
$areaMetric['centroid']['lng']
|
||||
);
|
||||
|
||||
if ($nearestDistance === null || $distance < $nearestDistance) {
|
||||
$nearestDistance = $distance;
|
||||
$assignedAreaId = $areaMetric['id'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($assignedAreaId !== null) {
|
||||
$areaRoadCounts[$assignedAreaId]++;
|
||||
if (in_array($road['condition_status'], ['minor_damage', 'major_damage'])) {
|
||||
$areaDamagedRoadCounts[$assignedAreaId]++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!$roadsExist) {
|
||||
$valueSource = 'dummy';
|
||||
}
|
||||
}
|
||||
|
||||
$data_items = [];
|
||||
$values = [];
|
||||
|
||||
foreach ($areaMetrics as $areaMetric) {
|
||||
$area_id = $areaMetric['id'];
|
||||
$geometry = $areaMetric['geometry'];
|
||||
$value = 0;
|
||||
|
||||
// Calculate value based on layer type
|
||||
if ($layer_type === 'population') {
|
||||
$value = $areaMetric['population_density'];
|
||||
|
||||
} else if ($layer_type === 'road_density') {
|
||||
if ($roadsExist) {
|
||||
$value = $areaRoadCounts[$area_id] / $areaMetric['area_km2'];
|
||||
} else {
|
||||
$value = deterministicRoadDensity($areaMetric['population_density'], $area_id);
|
||||
}
|
||||
|
||||
} else if ($layer_type === 'damaged_roads_density') {
|
||||
if ($roadsExist) {
|
||||
$value = $areaDamagedRoadCounts[$area_id] / $areaMetric['area_km2'];
|
||||
} else {
|
||||
$fallbackRoadDensity = deterministicRoadDensity($areaMetric['population_density'], $area_id);
|
||||
$value = deterministicDamagedDensity($fallbackRoadDensity, $area_id);
|
||||
}
|
||||
}
|
||||
|
||||
$value = (float)$value;
|
||||
$values[] = $value;
|
||||
|
||||
$data_items[] = [
|
||||
'area_id' => $area_id,
|
||||
'area_name' => $areaMetric['name'],
|
||||
'value' => $value,
|
||||
'geometry' => $geometry,
|
||||
'area_km2' => round($areaMetric['area_km2'], 6),
|
||||
'population' => $areaMetric['population'],
|
||||
'population_density' => round($areaMetric['population_density'], 6),
|
||||
'value_source' => $valueSource
|
||||
];
|
||||
}
|
||||
|
||||
// Calculate min and max values
|
||||
$min_value = count($values) > 0 ? min($values) : 0;
|
||||
$max_value = count($values) > 0 ? max($values) : 0;
|
||||
|
||||
// If all values are same, set reasonable range
|
||||
if ($min_value === $max_value && $min_value > 0) {
|
||||
$min_value = $min_value * 0.8;
|
||||
$max_value = $max_value * 1.2;
|
||||
} else if ($min_value === $max_value) {
|
||||
$min_value = 0;
|
||||
$max_value = 1;
|
||||
}
|
||||
|
||||
// Return success response
|
||||
http_response_code(200);
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'layer_type' => $layer_type,
|
||||
'data' => $data_items,
|
||||
'min_value' => floatval($min_value),
|
||||
'max_value' => floatval($max_value),
|
||||
'admin_level' => $admin_level,
|
||||
'value_source' => $valueSource
|
||||
]);
|
||||
|
||||
} catch (PDOException $e) {
|
||||
http_response_code(500);
|
||||
echo json_encode([
|
||||
'success' => false,
|
||||
'error' => 'Database error: ' . $e->getMessage()
|
||||
]);
|
||||
} catch (Exception $e) {
|
||||
http_response_code(400);
|
||||
echo json_encode([
|
||||
'success' => false,
|
||||
'error' => $e->getMessage()
|
||||
]);
|
||||
}
|
||||
?>
|
||||
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
header('Content-Type: application/json');
|
||||
|
||||
require_once 'config.php';
|
||||
|
||||
header('Access-Control-Allow-Origin: *');
|
||||
header('Access-Control-Allow-Methods: GET, OPTIONS');
|
||||
header('Access-Control-Allow-Headers: Content-Type');
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
|
||||
http_response_code(200);
|
||||
exit();
|
||||
}
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'GET') {
|
||||
http_response_code(405);
|
||||
die(json_encode(['error' => 'Method not allowed']));
|
||||
}
|
||||
|
||||
try {
|
||||
$db = getDB();
|
||||
|
||||
$stmt = $db->prepare('SELECT key_value FROM score_metadata WHERE key_name = ? LIMIT 1');
|
||||
$stmt->execute(['target_nasional']);
|
||||
$targetNasional = $stmt->fetchColumn();
|
||||
|
||||
$stmt->execute(['population_reference']);
|
||||
$populationReference = $stmt->fetchColumn();
|
||||
|
||||
if ($targetNasional === false || $targetNasional === null || $targetNasional === '') {
|
||||
$targetNasional = '7.50';
|
||||
}
|
||||
|
||||
if ($populationReference === false || $populationReference === null || $populationReference === '') {
|
||||
$populationReference = '288315899';
|
||||
}
|
||||
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'configuration' => [
|
||||
'target_nasional' => (float)$targetNasional,
|
||||
'population_reference' => (int)$populationReference
|
||||
]
|
||||
]);
|
||||
} catch (Exception $e) {
|
||||
http_response_code(500);
|
||||
echo json_encode(['error' => $e->getMessage()]);
|
||||
}
|
||||
?>
|
||||
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
header('Content-Type: application/json');
|
||||
require_once 'config.php';
|
||||
|
||||
// Handle CORS
|
||||
header('Access-Control-Allow-Origin: *');
|
||||
header('Access-Control-Allow-Methods: GET, OPTIONS');
|
||||
header('Access-Control-Allow-Headers: Content-Type');
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
|
||||
http_response_code(200);
|
||||
exit();
|
||||
}
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'GET') {
|
||||
$user = getCurrentUser();
|
||||
|
||||
if ($user) {
|
||||
http_response_code(200);
|
||||
die(json_encode(['success' => true, 'user' => $user]));
|
||||
} else {
|
||||
http_response_code(401);
|
||||
die(json_encode(['success' => false, 'user' => null]));
|
||||
}
|
||||
}
|
||||
|
||||
http_response_code(405);
|
||||
die(json_encode(['error' => 'Method not allowed']));
|
||||
@@ -0,0 +1,83 @@
|
||||
<?php
|
||||
header('Content-Type: application/json');
|
||||
require_once 'config.php';
|
||||
|
||||
// Get current user to restrict access (optional for MVP)
|
||||
$user = getCurrentUser();
|
||||
|
||||
try {
|
||||
$db = getDB();
|
||||
|
||||
// Get total households
|
||||
$householdTotal = $db->query('SELECT COUNT(*) as count FROM households')->fetch(PDO::FETCH_ASSOC)['count'];
|
||||
|
||||
// Get households by verification status
|
||||
$statusBreakdown = $db->query('
|
||||
SELECT verification_status, COUNT(*) as count
|
||||
FROM households
|
||||
GROUP BY verification_status
|
||||
')->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
$statusMap = [];
|
||||
foreach ($statusBreakdown as $row) {
|
||||
$statusMap[$row['verification_status']] = $row['count'];
|
||||
}
|
||||
|
||||
// Get average SKR (poverty score)
|
||||
$skrAvg = $db->query('
|
||||
SELECT
|
||||
AVG(skr) as avg_skr,
|
||||
COUNT(*) as scored_count
|
||||
FROM household_scores
|
||||
')->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
// Get households assisted
|
||||
$assistedCount = $db->query('
|
||||
SELECT COUNT(DISTINCT household_id) as count
|
||||
FROM assistance_distributions
|
||||
WHERE delivery_status = \'delivered\'
|
||||
')->fetch(PDO::FETCH_ASSOC)['count'];
|
||||
|
||||
// Get priority level breakdown
|
||||
$priorityBreakdown = $db->query('
|
||||
SELECT priority_level, COUNT(*) as count
|
||||
FROM household_scores
|
||||
GROUP BY priority_level
|
||||
')->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
$priorityMap = [];
|
||||
foreach ($priorityBreakdown as $row) {
|
||||
$priorityMap[$row['priority_level']] = $row['count'];
|
||||
}
|
||||
|
||||
// Get assistance type breakdown
|
||||
$assistanceTypes = $db->query('
|
||||
SELECT
|
||||
assistance_type,
|
||||
COUNT(*) as count,
|
||||
SUM(assistance_value) as total_value
|
||||
FROM assistance_distributions
|
||||
GROUP BY assistance_type
|
||||
')->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
$response = [
|
||||
'success' => true,
|
||||
'summary' => [
|
||||
'total_households' => $householdTotal,
|
||||
'avg_poverty_score' => round($skrAvg['avg_skr'] ?? 0, 2),
|
||||
'households_scored' => $skrAvg['scored_count'] ?? 0,
|
||||
'households_assisted' => $assistedCount,
|
||||
'percentage_assisted' => $householdTotal > 0 ? round(($assistedCount / $householdTotal) * 100, 1) : 0
|
||||
],
|
||||
'status_breakdown' => $statusMap,
|
||||
'priority_breakdown' => $priorityMap,
|
||||
'assistance_by_type' => $assistanceTypes
|
||||
];
|
||||
|
||||
http_response_code(200);
|
||||
die(json_encode($response));
|
||||
|
||||
} catch (PDOException $e) {
|
||||
http_response_code(500);
|
||||
die(json_encode(['error' => 'Database error: ' . $e->getMessage()]));
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
<?php
|
||||
header('Content-Type: application/json');
|
||||
require_once 'config.php';
|
||||
|
||||
// Get current user to restrict access
|
||||
$user = getCurrentUser();
|
||||
|
||||
try {
|
||||
$db = getDB();
|
||||
|
||||
// Get actual need points count from database (real poor population data)
|
||||
$needPointsStats = $db->query('SELECT COUNT(*) as total FROM need_points')->fetch(PDO::FETCH_ASSOC);
|
||||
$totalNeedPoints = (int)$needPointsStats['total'];
|
||||
|
||||
// Get configuration from database: population reference and target nasional
|
||||
$configStmt = $db->prepare('SELECT key_value FROM score_metadata WHERE key_name = ? LIMIT 1');
|
||||
|
||||
$configStmt->execute(['population_reference']);
|
||||
$populationReference = (int)$configStmt->fetchColumn();
|
||||
if (!$populationReference || $populationReference <= 0) {
|
||||
$populationReference = 288315899; // Default fallback
|
||||
}
|
||||
|
||||
$configStmt->execute(['target_nasional']);
|
||||
$targetNasional = (float)$configStmt->fetchColumn();
|
||||
if (!$targetNasional || $targetNasional <= 0) {
|
||||
$targetNasional = 7.50; // Default fallback
|
||||
}
|
||||
|
||||
// Calculate poverty percentage from real data
|
||||
$currentPovertyPercentage = ($totalNeedPoints / $populationReference) * 100;
|
||||
|
||||
$response = [
|
||||
'success' => true,
|
||||
'charts' => [
|
||||
// Chart 1: Absolute population comparison (real data from system)
|
||||
'chart1' => [
|
||||
'title' => 'Total Penduduk Miskin vs Total Populasi Indonesia',
|
||||
'type' => 'bar',
|
||||
'labels' => [
|
||||
'Penduduk Miskin',
|
||||
'Total Populasi Indonesia'
|
||||
],
|
||||
'data' => [
|
||||
$totalNeedPoints,
|
||||
$populationReference
|
||||
],
|
||||
'units' => [
|
||||
'jiwa',
|
||||
'jiwa'
|
||||
]
|
||||
],
|
||||
// Chart 2: Percentage comparison against national target (real data)
|
||||
'chart2' => [
|
||||
'title' => 'Persentase Kemiskinan vs Target Nasional',
|
||||
'type' => 'bar',
|
||||
'labels' => [
|
||||
'Persentase Kemiskinan',
|
||||
'Target Nasional'
|
||||
],
|
||||
'data' => [
|
||||
round($currentPovertyPercentage, 6),
|
||||
$targetNasional
|
||||
],
|
||||
'units' => [
|
||||
'%',
|
||||
'%'
|
||||
],
|
||||
'current_poverty' => round($currentPovertyPercentage, 6),
|
||||
'target' => $targetNasional
|
||||
]
|
||||
],
|
||||
'statistics' => [
|
||||
'total_need_points' => $totalNeedPoints,
|
||||
'population_reference' => $populationReference,
|
||||
'current_poverty_percentage' => round($currentPovertyPercentage, 6),
|
||||
'national_target' => $targetNasional
|
||||
]
|
||||
];
|
||||
|
||||
http_response_code(200);
|
||||
die(json_encode($response));
|
||||
|
||||
} catch (PDOException $e) {
|
||||
http_response_code(500);
|
||||
die(json_encode([
|
||||
'success' => false,
|
||||
'error' => 'Database error: ' . $e->getMessage()
|
||||
]));
|
||||
} catch (Exception $e) {
|
||||
http_response_code(400);
|
||||
die(json_encode([
|
||||
'success' => false,
|
||||
'error' => 'Error: ' . $e->getMessage()
|
||||
]));
|
||||
}
|
||||
?>
|
||||
|
||||
@@ -0,0 +1,123 @@
|
||||
<?php
|
||||
error_reporting(E_ALL);
|
||||
ini_set('display_errors', 0);
|
||||
header('Content-Type: application/json');
|
||||
|
||||
require_once 'config.php';
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'GET') {
|
||||
try {
|
||||
$db = getDB();
|
||||
syncNeedPointsToHouseholds($db);
|
||||
ensureHouseholdScoresForAllHouseholds($db);
|
||||
|
||||
$scoreCountStmt = $db->query('SELECT COUNT(*) AS count FROM household_scores');
|
||||
$scoreCount = (int)$scoreCountStmt->fetch(PDO::FETCH_ASSOC)['count'];
|
||||
|
||||
$sql = '
|
||||
SELECT
|
||||
hs.household_id,
|
||||
hs.skr,
|
||||
hs.sag,
|
||||
hs.spi,
|
||||
hs.priority_level,
|
||||
hs.score_version,
|
||||
hs.computed_at,
|
||||
h.household_code,
|
||||
h.head_name,
|
||||
h.economic_status,
|
||||
h.monthly_income,
|
||||
h.dependents_count,
|
||||
h.verification_status,
|
||||
CASE WHEN EXISTS (
|
||||
SELECT 1
|
||||
FROM assistance_distributions ad
|
||||
WHERE ad.household_id = hs.household_id
|
||||
AND ad.delivery_status = \'delivered\'
|
||||
) THEN 1 ELSE 0 END AS assisted,
|
||||
m.name AS masjid_name,
|
||||
pa.name AS area_name
|
||||
FROM household_scores hs
|
||||
JOIN households h ON h.id = hs.household_id
|
||||
LEFT JOIN masjids m ON m.id = h.masjid_id
|
||||
LEFT JOIN pontianak_areas pa ON pa.id = h.pontianak_area_id
|
||||
WHERE 1 = 1
|
||||
';
|
||||
|
||||
$params = [];
|
||||
|
||||
if (isset($_GET['priority_level']) && $_GET['priority_level'] !== '') {
|
||||
$allowed = ['very_high', 'high', 'medium', 'low'];
|
||||
$priority = strtolower(trim((string)$_GET['priority_level']));
|
||||
if (!in_array($priority, $allowed, true)) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'priority_level is invalid']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$sql .= ' AND hs.priority_level = ?';
|
||||
$params[] = $priority;
|
||||
}
|
||||
|
||||
if (isset($_GET['min_spi']) && $_GET['min_spi'] !== '') {
|
||||
if (!is_numeric($_GET['min_spi'])) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'min_spi must be numeric']);
|
||||
exit;
|
||||
}
|
||||
$minSpi = (float)$_GET['min_spi'];
|
||||
$sql .= ' AND hs.spi >= ?';
|
||||
$params[] = $minSpi;
|
||||
}
|
||||
|
||||
$includeAssisted = isset($_GET['include_assisted']) && $_GET['include_assisted'] !== '' && $_GET['include_assisted'] !== '0';
|
||||
if (!$includeAssisted) {
|
||||
$sql .= ' AND NOT EXISTS (SELECT 1 FROM assistance_distributions ad WHERE ad.household_id = hs.household_id AND ad.delivery_status = \'delivered\')';
|
||||
}
|
||||
|
||||
$limit = 100;
|
||||
if (isset($_GET['limit']) && $_GET['limit'] !== '') {
|
||||
if (!is_numeric($_GET['limit'])) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'limit must be numeric']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$limit = max(1, min((int)$_GET['limit'], 5000));
|
||||
}
|
||||
|
||||
$offset = 0;
|
||||
if (isset($_GET['offset']) && $_GET['offset'] !== '') {
|
||||
if (!is_numeric($_GET['offset'])) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'offset must be numeric']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$offset = max(0, (int)$_GET['offset']);
|
||||
}
|
||||
|
||||
$sql .= ' ORDER BY hs.spi DESC, hs.skr DESC, hs.computed_at DESC LIMIT ' . $limit . ' OFFSET ' . $offset;
|
||||
|
||||
$stmt = $db->prepare($sql);
|
||||
$stmt->execute($params);
|
||||
$ranking = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'count' => count($ranking),
|
||||
'limit' => $limit,
|
||||
'offset' => $offset,
|
||||
'assisted_count' => (int)$db->query("SELECT COUNT(DISTINCT household_id) AS count FROM assistance_distributions WHERE delivery_status = 'delivered'")->fetch(PDO::FETCH_ASSOC)['count'],
|
||||
'ranking' => $ranking,
|
||||
'score_version' => 'v1'
|
||||
]);
|
||||
} catch (Exception $e) {
|
||||
http_response_code(500);
|
||||
echo json_encode(['error' => $e->getMessage()]);
|
||||
}
|
||||
} else {
|
||||
http_response_code(405);
|
||||
echo json_encode(['error' => 'Method not allowed']);
|
||||
}
|
||||
?>
|
||||
@@ -0,0 +1,104 @@
|
||||
<?php
|
||||
error_reporting(E_ALL);
|
||||
ini_set('display_errors', 0);
|
||||
header('Content-Type: application/json');
|
||||
|
||||
require_once 'config.php';
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'GET') {
|
||||
try {
|
||||
$db = getDB();
|
||||
|
||||
$syncResult = syncNeedPointsToHouseholds($db);
|
||||
$computedMissing = ensureHouseholdScoresForAllHouseholds($db);
|
||||
|
||||
$totalStmt = $db->query('SELECT COUNT(*) AS total_households FROM households');
|
||||
$totalHouseholds = (int)$totalStmt->fetch(PDO::FETCH_ASSOC)['total_households'];
|
||||
|
||||
$scoreTotalStmt = $db->query('SELECT COUNT(*) AS scored_households FROM household_scores');
|
||||
$scoredHouseholds = (int)$scoreTotalStmt->fetch(PDO::FETCH_ASSOC)['scored_households'];
|
||||
|
||||
$assistedStmt = $db->query("\n SELECT COUNT(DISTINCT household_id) AS assisted_households\n FROM assistance_distributions\n WHERE delivery_status = 'delivered'\n ");
|
||||
$assistedHouseholds = (int)$assistedStmt->fetch(PDO::FETCH_ASSOC)['assisted_households'];
|
||||
|
||||
$priorityStmt = $db->query('
|
||||
SELECT
|
||||
priority_level,
|
||||
COUNT(*) AS count
|
||||
FROM household_scores hs
|
||||
JOIN households h ON h.id = hs.household_id
|
||||
WHERE NOT EXISTS (
|
||||
SELECT 1
|
||||
FROM assistance_distributions ad
|
||||
WHERE ad.household_id = hs.household_id
|
||||
AND ad.delivery_status = \'delivered\'
|
||||
)
|
||||
GROUP BY priority_level
|
||||
');
|
||||
$priorityRows = $priorityStmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
$priorityCounts = [
|
||||
'very_high' => 0,
|
||||
'high' => 0,
|
||||
'medium' => 0,
|
||||
'low' => 0
|
||||
];
|
||||
|
||||
foreach ($priorityRows as $row) {
|
||||
$priority = $row['priority_level'];
|
||||
$priorityCounts[$priority] = (int)$row['count'];
|
||||
}
|
||||
|
||||
$avgStmt = $db->query('
|
||||
SELECT
|
||||
COALESCE(AVG(skr), 0) AS avg_skr,
|
||||
COALESCE(AVG(sag), 0) AS avg_sag,
|
||||
COALESCE(AVG(spi), 0) AS avg_spi,
|
||||
MAX(computed_at) AS latest_computed_at
|
||||
FROM household_scores
|
||||
');
|
||||
$averages = $avgStmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
$topStmt = $db->query('
|
||||
SELECT
|
||||
hs.household_id,
|
||||
h.household_code,
|
||||
h.head_name,
|
||||
hs.spi,
|
||||
hs.priority_level
|
||||
FROM household_scores hs
|
||||
JOIN households h ON h.id = hs.household_id
|
||||
WHERE NOT EXISTS (
|
||||
SELECT 1
|
||||
FROM assistance_distributions ad
|
||||
WHERE ad.household_id = hs.household_id
|
||||
AND ad.delivery_status = \'delivered\'
|
||||
)
|
||||
ORDER BY hs.spi DESC, hs.skr DESC
|
||||
LIMIT 5
|
||||
');
|
||||
$topPriorities = $topStmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'summary' => [
|
||||
'total_households' => $totalHouseholds,
|
||||
'scored_households' => $scoredHouseholds,
|
||||
'assisted_households' => $assistedHouseholds,
|
||||
'priority_counts' => $priorityCounts,
|
||||
'averages' => $averages,
|
||||
'top_priorities' => $topPriorities,
|
||||
'score_version' => 'v1',
|
||||
'sync_result' => $syncResult,
|
||||
'computed_missing_scores' => $computedMissing
|
||||
]
|
||||
]);
|
||||
} catch (Exception $e) {
|
||||
http_response_code(500);
|
||||
echo json_encode(['error' => $e->getMessage()]);
|
||||
}
|
||||
} else {
|
||||
http_response_code(405);
|
||||
echo json_encode(['error' => 'Method not allowed']);
|
||||
}
|
||||
?>
|
||||
@@ -0,0 +1,74 @@
|
||||
<?php
|
||||
error_reporting(E_ALL);
|
||||
ini_set('display_errors', 0);
|
||||
header('Content-Type: application/json');
|
||||
|
||||
require_once 'config.php';
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'GET') {
|
||||
try {
|
||||
$db = getDB();
|
||||
|
||||
$totalsStmt = $db->query('
|
||||
SELECT
|
||||
COUNT(*) AS total_households,
|
||||
SUM(CASE WHEN verification_status = \'unverified\' THEN 1 ELSE 0 END) AS unverified_count,
|
||||
SUM(CASE WHEN verification_status = \'field_verified\' THEN 1 ELSE 0 END) AS field_verified_count,
|
||||
SUM(CASE WHEN verification_status = \'admin_approved\' THEN 1 ELSE 0 END) AS admin_approved_count,
|
||||
SUM(CASE WHEN verification_status = \'rejected\' THEN 1 ELSE 0 END) AS rejected_count,
|
||||
SUM(CASE WHEN verification_status = \'needs_review\' THEN 1 ELSE 0 END) AS needs_review_count,
|
||||
COALESCE(AVG(monthly_income), 0) AS avg_monthly_income
|
||||
FROM households
|
||||
');
|
||||
$totals = $totalsStmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
$assistanceStmt = $db->query('
|
||||
SELECT
|
||||
COUNT(*) AS total_distributions,
|
||||
COUNT(DISTINCT household_id) AS assisted_households,
|
||||
COALESCE(SUM(assistance_value), 0) AS total_assistance_value,
|
||||
COALESCE(SUM(CASE WHEN delivery_status = \'delivered\' THEN assistance_value ELSE 0 END), 0) AS delivered_assistance_value
|
||||
FROM assistance_distributions
|
||||
');
|
||||
$assistance = $assistanceStmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
$verificationStmt = $db->query('
|
||||
SELECT
|
||||
COUNT(*) AS total_verifications,
|
||||
SUM(CASE WHEN DATE(verified_at) >= DATE(\'now\', \'-7 day\') THEN 1 ELSE 0 END) AS verifications_last_7_days,
|
||||
COALESCE(AVG(data_confidence_score), 0) AS avg_confidence_score
|
||||
FROM verification_logs
|
||||
');
|
||||
$verification = $verificationStmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
$topMasjidStmt = $db->query('
|
||||
SELECT
|
||||
m.id,
|
||||
m.name,
|
||||
COUNT(h.id) AS household_count
|
||||
FROM masjids m
|
||||
LEFT JOIN households h ON h.masjid_id = m.id
|
||||
GROUP BY m.id, m.name
|
||||
ORDER BY household_count DESC, m.name ASC
|
||||
LIMIT 5
|
||||
');
|
||||
$topMasjidCoverage = $topMasjidStmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'summary' => [
|
||||
'totals' => $totals,
|
||||
'assistance' => $assistance,
|
||||
'verification' => $verification,
|
||||
'top_masjid_coverage' => $topMasjidCoverage
|
||||
]
|
||||
]);
|
||||
} catch (Exception $e) {
|
||||
http_response_code(500);
|
||||
echo json_encode(['error' => $e->getMessage()]);
|
||||
}
|
||||
} else {
|
||||
http_response_code(405);
|
||||
echo json_encode(['error' => 'Method not allowed']);
|
||||
}
|
||||
?>
|
||||
@@ -0,0 +1,111 @@
|
||||
<?php
|
||||
error_reporting(E_ALL);
|
||||
ini_set('display_errors', 0);
|
||||
header('Content-Type: application/json');
|
||||
|
||||
require_once 'config.php';
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'GET') {
|
||||
try {
|
||||
$db = getDB();
|
||||
syncNeedPointsToHouseholds($db);
|
||||
ensureHouseholdScoresForAllHouseholds($db);
|
||||
|
||||
$sql = '
|
||||
SELECT
|
||||
h.*,
|
||||
np.name AS need_point_name,
|
||||
m.name AS masjid_name,
|
||||
pa.name AS area_name,
|
||||
(
|
||||
SELECT COUNT(*)
|
||||
FROM assistance_distributions ad
|
||||
WHERE ad.household_id = h.id
|
||||
) AS assistance_count,
|
||||
(
|
||||
SELECT MAX(vl.verified_at)
|
||||
FROM verification_logs vl
|
||||
WHERE vl.household_id = h.id
|
||||
) AS last_verified_at
|
||||
FROM households h
|
||||
LEFT JOIN need_points np ON np.id = h.need_point_id
|
||||
LEFT JOIN masjids m ON m.id = h.masjid_id
|
||||
LEFT JOIN pontianak_areas pa ON pa.id = h.pontianak_area_id
|
||||
WHERE 1 = 1
|
||||
';
|
||||
|
||||
$params = [];
|
||||
|
||||
if (isset($_GET['verification_status']) && $_GET['verification_status'] !== '') {
|
||||
$error = null;
|
||||
$status = normalizeVerificationStatus($_GET['verification_status'], $error, true);
|
||||
if ($status === null) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => $error]);
|
||||
exit;
|
||||
}
|
||||
|
||||
$sql .= ' AND h.verification_status = ?';
|
||||
$params[] = $status;
|
||||
}
|
||||
|
||||
if (isset($_GET['masjid_id']) && $_GET['masjid_id'] !== '') {
|
||||
if (!is_numeric($_GET['masjid_id'])) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'masjid_id must be numeric']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$sql .= ' AND h.masjid_id = ?';
|
||||
$params[] = (int)$_GET['masjid_id'];
|
||||
}
|
||||
|
||||
if (isset($_GET['pontianak_area_id']) && $_GET['pontianak_area_id'] !== '') {
|
||||
if (!is_numeric($_GET['pontianak_area_id'])) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'pontianak_area_id must be numeric']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$sql .= ' AND h.pontianak_area_id = ?';
|
||||
$params[] = (int)$_GET['pontianak_area_id'];
|
||||
}
|
||||
|
||||
if (isset($_GET['q']) && trim((string)$_GET['q']) !== '') {
|
||||
$q = '%' . trim((string)$_GET['q']) . '%';
|
||||
$sql .= ' AND (h.household_code LIKE ? OR h.head_name LIKE ?)';
|
||||
$params[] = $q;
|
||||
$params[] = $q;
|
||||
}
|
||||
|
||||
$limit = 200;
|
||||
if (isset($_GET['limit']) && $_GET['limit'] !== '') {
|
||||
if (!is_numeric($_GET['limit'])) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'limit must be numeric']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$limit = max(1, min((int)$_GET['limit'], 1000));
|
||||
}
|
||||
|
||||
$sql .= ' ORDER BY h.created_at DESC LIMIT ' . $limit;
|
||||
|
||||
$stmt = $db->prepare($sql);
|
||||
$stmt->execute($params);
|
||||
$households = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'count' => count($households),
|
||||
'households' => $households
|
||||
]);
|
||||
} catch (Exception $e) {
|
||||
http_response_code(500);
|
||||
echo json_encode(['error' => $e->getMessage()]);
|
||||
}
|
||||
} else {
|
||||
http_response_code(405);
|
||||
echo json_encode(['error' => 'Method not allowed']);
|
||||
}
|
||||
?>
|
||||
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
error_reporting(E_ALL);
|
||||
ini_set('display_errors', 0);
|
||||
header('Content-Type: application/json');
|
||||
|
||||
require_once 'config.php';
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'GET') {
|
||||
try {
|
||||
$db = getDB();
|
||||
$stmt = $db->query('SELECT id, name, road_type, coordinates, length_meters, created_at, updated_at FROM roads ORDER BY created_at DESC');
|
||||
$roads = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
// Decode coordinates JSON for each road
|
||||
foreach ($roads as &$road) {
|
||||
$road['coordinates'] = json_decode($road['coordinates'], true);
|
||||
}
|
||||
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'lines' => $roads
|
||||
]);
|
||||
} catch (Exception $e) {
|
||||
http_response_code(500);
|
||||
echo json_encode(['error' => $e->getMessage()]);
|
||||
}
|
||||
} else {
|
||||
http_response_code(405);
|
||||
echo json_encode(['error' => 'Method not allowed']);
|
||||
}
|
||||
?>
|
||||
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
error_reporting(E_ALL);
|
||||
ini_set('display_errors', 0);
|
||||
header('Content-Type: application/json');
|
||||
|
||||
require_once 'config.php';
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'GET') {
|
||||
try {
|
||||
$db = getDB();
|
||||
$stmt = $db->query('
|
||||
SELECT
|
||||
m.id,
|
||||
m.name,
|
||||
m.latitude,
|
||||
m.longitude,
|
||||
m.radius_meters,
|
||||
m.created_at,
|
||||
m.updated_at,
|
||||
COUNT(np.id) AS need_point_count
|
||||
FROM masjids m
|
||||
LEFT JOIN need_points np ON np.masjid_id = m.id
|
||||
GROUP BY m.id
|
||||
ORDER BY m.created_at DESC
|
||||
');
|
||||
|
||||
$masjids = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'masjids' => $masjids
|
||||
]);
|
||||
} catch (Exception $e) {
|
||||
http_response_code(500);
|
||||
echo json_encode(['error' => $e->getMessage()]);
|
||||
}
|
||||
} else {
|
||||
http_response_code(405);
|
||||
echo json_encode(['error' => 'Method not allowed']);
|
||||
}
|
||||
?>
|
||||
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
error_reporting(E_ALL);
|
||||
ini_set('display_errors', 0);
|
||||
header('Content-Type: application/json');
|
||||
|
||||
require_once 'config.php';
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'GET') {
|
||||
try {
|
||||
$db = getDB();
|
||||
$stmt = $db->query("\n SELECT\n np.id,\n np.name,\n np.latitude,\n np.longitude,\n np.masjid_id,\n np.household_name,\n np.head_name,\n np.economic_status,\n np.is_verified,\n CASE WHEN EXISTS (\n SELECT 1\n FROM households h\n JOIN assistance_distributions ad ON ad.household_id = h.id\n WHERE h.need_point_id = np.id\n AND ad.delivery_status = 'delivered'\n ) THEN 1 ELSE 0 END AS assisted,\n np.created_at,\n np.updated_at,\n m.name AS masjid_name,\n m.latitude AS masjid_latitude,\n m.longitude AS masjid_longitude,\n m.radius_meters\n FROM need_points np\n JOIN masjids m ON m.id = np.masjid_id\n ORDER BY np.created_at DESC\n ");
|
||||
|
||||
$points = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
foreach ($points as &$point) {
|
||||
$point['distance_meters'] = haversineDistanceMeters(
|
||||
(float)$point['latitude'],
|
||||
(float)$point['longitude'],
|
||||
(float)$point['masjid_latitude'],
|
||||
(float)$point['masjid_longitude']
|
||||
);
|
||||
}
|
||||
unset($point);
|
||||
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'points' => $points
|
||||
]);
|
||||
} catch (Exception $e) {
|
||||
http_response_code(500);
|
||||
echo json_encode(['error' => $e->getMessage()]);
|
||||
}
|
||||
} else {
|
||||
http_response_code(405);
|
||||
echo json_encode(['error' => 'Method not allowed']);
|
||||
}
|
||||
?>
|
||||
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
error_reporting(E_ALL);
|
||||
ini_set('display_errors', 0);
|
||||
header('Content-Type: application/json');
|
||||
|
||||
require_once 'config.php';
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'GET') {
|
||||
try {
|
||||
$db = getDB();
|
||||
$stmt = $db->query('SELECT id, name, nomor_spbu, latitude, longitude, open_24_hours, created_at FROM markers ORDER BY created_at DESC');
|
||||
$markers = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'markers' => $markers
|
||||
]);
|
||||
} catch (Exception $e) {
|
||||
http_response_code(500);
|
||||
echo json_encode(['error' => $e->getMessage()]);
|
||||
}
|
||||
} else {
|
||||
http_response_code(405);
|
||||
echo json_encode(['error' => 'Method not allowed']);
|
||||
}
|
||||
?>
|
||||
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
error_reporting(E_ALL);
|
||||
ini_set('display_errors', 0);
|
||||
header('Content-Type: application/json');
|
||||
|
||||
require_once 'config.php';
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'GET') {
|
||||
try {
|
||||
$db = getDB();
|
||||
$stmt = $db->query('SELECT id, name, certificate_type, coordinates, area_sqm, created_at, updated_at FROM land_parcels ORDER BY created_at DESC');
|
||||
$parcels = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
// Decode coordinates JSON for each parcel
|
||||
foreach ($parcels as &$parcel) {
|
||||
$parcel['coordinates'] = json_decode($parcel['coordinates'], true);
|
||||
}
|
||||
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'polygons' => $parcels
|
||||
]);
|
||||
} catch (Exception $e) {
|
||||
http_response_code(500);
|
||||
echo json_encode(['error' => $e->getMessage()]);
|
||||
}
|
||||
} else {
|
||||
http_response_code(405);
|
||||
echo json_encode(['error' => 'Method not allowed']);
|
||||
}
|
||||
?>
|
||||
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
header('Content-Type: application/json');
|
||||
require_once 'config.php';
|
||||
|
||||
try {
|
||||
$db = getDB();
|
||||
|
||||
// Get optional filter parameter
|
||||
$area_type = isset($_GET['area_type']) ? $_GET['area_type'] : '';
|
||||
|
||||
// Build SQL query
|
||||
$sql = 'SELECT id, name, area_type, geometry, population, created_at, updated_at FROM pontianak_areas';
|
||||
|
||||
if ($area_type) {
|
||||
$sql .= ' WHERE area_type = ?';
|
||||
$stmt = $db->prepare($sql);
|
||||
$stmt->execute([$area_type]);
|
||||
} else {
|
||||
$stmt = $db->prepare($sql);
|
||||
$stmt->execute();
|
||||
}
|
||||
|
||||
$areas = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
// Decode geometry JSON for each area
|
||||
foreach ($areas as &$area) {
|
||||
$area['geometry'] = json_decode($area['geometry'], true);
|
||||
}
|
||||
|
||||
// Return success response
|
||||
http_response_code(200);
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'areas' => $areas
|
||||
]);
|
||||
|
||||
} catch (PDOException $e) {
|
||||
http_response_code(500);
|
||||
echo json_encode([
|
||||
'success' => false,
|
||||
'error' => 'Database error: ' . $e->getMessage()
|
||||
]);
|
||||
} catch (Exception $e) {
|
||||
http_response_code(400);
|
||||
echo json_encode([
|
||||
'success' => false,
|
||||
'error' => $e->getMessage()
|
||||
]);
|
||||
}
|
||||
?>
|
||||
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
header('Content-Type: application/json');
|
||||
require_once 'config.php';
|
||||
|
||||
// Check authentication and authorization
|
||||
requireRole(['Admin']);
|
||||
|
||||
try {
|
||||
$db = getDB();
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'GET') {
|
||||
// Get all users
|
||||
$stmt = $db->query('
|
||||
SELECT id, username, email, role, is_active, created_at
|
||||
FROM users
|
||||
ORDER BY created_at DESC
|
||||
');
|
||||
$users = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
http_response_code(200);
|
||||
die(json_encode([
|
||||
'success' => true,
|
||||
'users' => $users
|
||||
]));
|
||||
}
|
||||
|
||||
http_response_code(405);
|
||||
die(json_encode(['error' => 'Method not allowed']));
|
||||
|
||||
} catch (PDOException $e) {
|
||||
http_response_code(500);
|
||||
die(json_encode(['error' => 'Database error: ' . $e->getMessage()]));
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
<?php
|
||||
error_reporting(E_ALL);
|
||||
ini_set('display_errors', 0);
|
||||
header('Content-Type: application/json');
|
||||
|
||||
require_once 'config.php';
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'GET') {
|
||||
try {
|
||||
$db = getDB();
|
||||
|
||||
$sql = '
|
||||
SELECT
|
||||
vl.*,
|
||||
h.household_code,
|
||||
h.head_name,
|
||||
h.verification_status AS current_household_status
|
||||
FROM verification_logs vl
|
||||
JOIN households h ON h.id = vl.household_id
|
||||
WHERE 1 = 1
|
||||
';
|
||||
$params = [];
|
||||
|
||||
if (isset($_GET['household_id']) && $_GET['household_id'] !== '') {
|
||||
if (!is_numeric($_GET['household_id'])) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'household_id must be numeric']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$sql .= ' AND vl.household_id = ?';
|
||||
$params[] = (int)$_GET['household_id'];
|
||||
}
|
||||
|
||||
if (isset($_GET['verification_status']) && $_GET['verification_status'] !== '') {
|
||||
$error = null;
|
||||
$status = normalizeVerificationStatus($_GET['verification_status'], $error, false);
|
||||
if ($status === null) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => $error]);
|
||||
exit;
|
||||
}
|
||||
|
||||
$sql .= ' AND vl.verification_status = ?';
|
||||
$params[] = $status;
|
||||
}
|
||||
|
||||
if (isset($_GET['verifier_role']) && trim((string)$_GET['verifier_role']) !== '') {
|
||||
$sql .= ' AND vl.verifier_role = ?';
|
||||
$params[] = trim((string)$_GET['verifier_role']);
|
||||
}
|
||||
|
||||
if (isset($_GET['date_from']) && $_GET['date_from'] !== '') {
|
||||
$dateFrom = trim((string)$_GET['date_from']);
|
||||
$dateObj = DateTime::createFromFormat('Y-m-d', $dateFrom);
|
||||
if (!$dateObj || $dateObj->format('Y-m-d') !== $dateFrom) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'date_from must be in YYYY-MM-DD format']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$sql .= ' AND DATE(vl.verified_at) >= ?';
|
||||
$params[] = $dateFrom;
|
||||
}
|
||||
|
||||
if (isset($_GET['date_to']) && $_GET['date_to'] !== '') {
|
||||
$dateTo = trim((string)$_GET['date_to']);
|
||||
$dateObj = DateTime::createFromFormat('Y-m-d', $dateTo);
|
||||
if (!$dateObj || $dateObj->format('Y-m-d') !== $dateTo) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'date_to must be in YYYY-MM-DD format']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$sql .= ' AND DATE(vl.verified_at) <= ?';
|
||||
$params[] = $dateTo;
|
||||
}
|
||||
|
||||
$limit = 200;
|
||||
if (isset($_GET['limit']) && $_GET['limit'] !== '') {
|
||||
if (!is_numeric($_GET['limit'])) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'limit must be numeric']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$limit = max(1, min((int)$_GET['limit'], 1000));
|
||||
}
|
||||
|
||||
$sql .= ' ORDER BY vl.verified_at DESC, vl.created_at DESC LIMIT ' . $limit;
|
||||
|
||||
$stmt = $db->prepare($sql);
|
||||
$stmt->execute($params);
|
||||
$logs = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'count' => count($logs),
|
||||
'verification_logs' => $logs
|
||||
]);
|
||||
} catch (Exception $e) {
|
||||
http_response_code(500);
|
||||
echo json_encode(['error' => $e->getMessage()]);
|
||||
}
|
||||
} else {
|
||||
http_response_code(405);
|
||||
echo json_encode(['error' => 'Method not allowed']);
|
||||
}
|
||||
?>
|
||||
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
header('Content-Type: application/json');
|
||||
require_once 'config.php';
|
||||
|
||||
// Handle CORS
|
||||
header('Access-Control-Allow-Origin: *');
|
||||
header('Access-Control-Allow-Methods: POST, GET, OPTIONS');
|
||||
header('Access-Control-Allow-Headers: Content-Type');
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
|
||||
http_response_code(200);
|
||||
exit();
|
||||
}
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$data = json_decode(file_get_contents('php://input'), true);
|
||||
|
||||
if (!isset($data['email']) || !isset($data['password'])) {
|
||||
http_response_code(400);
|
||||
die(json_encode(['error' => 'Email and password are required']));
|
||||
}
|
||||
|
||||
$result = loginUser($data['email'], $data['password']);
|
||||
|
||||
if ($result['success']) {
|
||||
http_response_code(200);
|
||||
} else {
|
||||
http_response_code(401);
|
||||
}
|
||||
|
||||
die(json_encode($result));
|
||||
}
|
||||
|
||||
http_response_code(405);
|
||||
die(json_encode(['error' => 'Method not allowed']));
|
||||
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
header('Content-Type: application/json');
|
||||
require_once 'config.php';
|
||||
|
||||
// Handle CORS
|
||||
header('Access-Control-Allow-Origin: *');
|
||||
header('Access-Control-Allow-Methods: POST, GET, OPTIONS');
|
||||
header('Access-Control-Allow-Headers: Content-Type');
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
|
||||
http_response_code(200);
|
||||
exit();
|
||||
}
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$result = logoutUser();
|
||||
http_response_code(200);
|
||||
die(json_encode($result));
|
||||
}
|
||||
|
||||
http_response_code(405);
|
||||
die(json_encode(['error' => 'Method not allowed']));
|
||||
@@ -0,0 +1,134 @@
|
||||
<?php
|
||||
error_reporting(E_ALL);
|
||||
ini_set('display_errors', 0);
|
||||
header('Content-Type: application/json');
|
||||
|
||||
require_once 'config.php';
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
try {
|
||||
$data = json_decode(file_get_contents('php://input'), true);
|
||||
|
||||
if (!is_array($data)) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'Invalid JSON body']);
|
||||
exit;
|
||||
}
|
||||
|
||||
if (!isset($data['household_id']) || !is_numeric($data['household_id'])) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'Missing or invalid household_id']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$householdId = (int)$data['household_id'];
|
||||
|
||||
$error = null;
|
||||
$assistanceType = normalizeAssistanceType($data['assistance_type'] ?? null, $error);
|
||||
if ($assistanceType === null) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => $error]);
|
||||
exit;
|
||||
}
|
||||
|
||||
$assistanceValue = normalizeAssistanceValue($data['assistance_value'] ?? null, $error);
|
||||
if ($assistanceValue === null) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => $error]);
|
||||
exit;
|
||||
}
|
||||
|
||||
$assistanceFrequency = normalizeAssistanceFrequency($data['assistance_frequency'] ?? null, $error);
|
||||
if ($assistanceFrequency === null) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => $error]);
|
||||
exit;
|
||||
}
|
||||
|
||||
$deliveryStatus = normalizeDeliveryStatus($data['delivery_status'] ?? null, $error);
|
||||
if ($deliveryStatus === null) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => $error]);
|
||||
exit;
|
||||
}
|
||||
|
||||
$distributionDate = isset($data['distribution_date']) && trim((string)$data['distribution_date']) !== ''
|
||||
? trim((string)$data['distribution_date'])
|
||||
: date('Y-m-d');
|
||||
|
||||
$dateObj = DateTime::createFromFormat('Y-m-d', $distributionDate);
|
||||
if (!$dateObj || $dateObj->format('Y-m-d') !== $distributionDate) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'distribution_date must be in YYYY-MM-DD format']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$deliveredBy = isset($data['delivered_by']) ? trim((string)$data['delivered_by']) : null;
|
||||
if ($deliveredBy === '') {
|
||||
$deliveredBy = null;
|
||||
}
|
||||
|
||||
$notes = isset($data['notes']) ? trim((string)$data['notes']) : null;
|
||||
if ($notes === '') {
|
||||
$notes = null;
|
||||
}
|
||||
|
||||
$db = getDB();
|
||||
if (!householdExists($db, $householdId)) {
|
||||
http_response_code(404);
|
||||
echo json_encode(['error' => 'Household not found']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$insertStmt = $db->prepare('
|
||||
INSERT INTO assistance_distributions (
|
||||
household_id,
|
||||
assistance_type,
|
||||
assistance_value,
|
||||
assistance_frequency,
|
||||
distribution_date,
|
||||
delivery_status,
|
||||
delivered_by,
|
||||
notes,
|
||||
updated_at
|
||||
)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP)
|
||||
');
|
||||
|
||||
$insertStmt->execute([
|
||||
$householdId,
|
||||
$assistanceType,
|
||||
$assistanceValue,
|
||||
$assistanceFrequency,
|
||||
$distributionDate,
|
||||
$deliveryStatus,
|
||||
$deliveredBy,
|
||||
$notes
|
||||
]);
|
||||
|
||||
$id = (int)$db->lastInsertId();
|
||||
$resultStmt = $db->prepare('
|
||||
SELECT
|
||||
ad.*,
|
||||
h.household_code,
|
||||
h.head_name
|
||||
FROM assistance_distributions ad
|
||||
JOIN households h ON h.id = ad.household_id
|
||||
WHERE ad.id = ?
|
||||
');
|
||||
$resultStmt->execute([$id]);
|
||||
$distribution = $resultStmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'distribution' => $distribution
|
||||
]);
|
||||
} catch (Exception $e) {
|
||||
http_response_code(500);
|
||||
echo json_encode(['error' => $e->getMessage()]);
|
||||
}
|
||||
} else {
|
||||
http_response_code(405);
|
||||
echo json_encode(['error' => 'Method not allowed']);
|
||||
}
|
||||
?>
|
||||
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
header('Content-Type: application/json');
|
||||
|
||||
require_once 'config.php';
|
||||
|
||||
header('Access-Control-Allow-Origin: *');
|
||||
header('Access-Control-Allow-Methods: POST, OPTIONS');
|
||||
header('Access-Control-Allow-Headers: Content-Type');
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
|
||||
http_response_code(200);
|
||||
exit();
|
||||
}
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||||
http_response_code(405);
|
||||
die(json_encode(['error' => 'Method not allowed']));
|
||||
}
|
||||
|
||||
requireRole('Admin');
|
||||
|
||||
try {
|
||||
$data = json_decode(file_get_contents('php://input'), true);
|
||||
$targetNasional = isset($data['target_nasional']) ? (float)$data['target_nasional'] : null;
|
||||
$populationReference = isset($data['population_reference']) ? (int)$data['population_reference'] : null;
|
||||
|
||||
if ($targetNasional === null || $targetNasional < 0 || $targetNasional > 100) {
|
||||
http_response_code(400);
|
||||
die(json_encode(['error' => 'Target nasional must be between 0 and 100']));
|
||||
}
|
||||
|
||||
if ($populationReference === null || $populationReference < 1) {
|
||||
http_response_code(400);
|
||||
die(json_encode(['error' => 'Population reference must be greater than 0']));
|
||||
}
|
||||
|
||||
$db = getDB();
|
||||
$stmt = $db->prepare('INSERT INTO score_metadata (key_name, key_value, updated_at) VALUES (?, ?, CURRENT_TIMESTAMP) ON CONFLICT(key_name) DO UPDATE SET key_value = excluded.key_value, updated_at = CURRENT_TIMESTAMP');
|
||||
$stmt->execute(['target_nasional', number_format($targetNasional, 2, '.', '')]);
|
||||
$stmt->execute(['population_reference', (string)$populationReference]);
|
||||
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'configuration' => [
|
||||
'target_nasional' => $targetNasional,
|
||||
'population_reference' => $populationReference
|
||||
]
|
||||
]);
|
||||
} catch (Exception $e) {
|
||||
http_response_code(500);
|
||||
echo json_encode(['error' => $e->getMessage()]);
|
||||
}
|
||||
?>
|
||||
@@ -0,0 +1,240 @@
|
||||
<?php
|
||||
error_reporting(E_ALL);
|
||||
ini_set('display_errors', 0);
|
||||
header('Content-Type: application/json');
|
||||
|
||||
require_once 'config.php';
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
try {
|
||||
$data = json_decode(file_get_contents('php://input'), true);
|
||||
|
||||
if (!is_array($data)) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'Invalid JSON body']);
|
||||
exit;
|
||||
}
|
||||
|
||||
if (!isset($data['head_name']) || trim((string)$data['head_name']) === '') {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'Missing required field: head_name']);
|
||||
exit;
|
||||
}
|
||||
|
||||
if (!isset($data['latitude']) || !isset($data['longitude'])) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'Missing required fields: latitude and longitude']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$error = null;
|
||||
if (!validateCoordinates($data['latitude'], $data['longitude'], $error)) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => $error]);
|
||||
exit;
|
||||
}
|
||||
|
||||
$db = getDB();
|
||||
|
||||
$headName = trim((string)$data['head_name']);
|
||||
$latitude = (float)$data['latitude'];
|
||||
$longitude = (float)$data['longitude'];
|
||||
|
||||
$monthlyIncome = normalizeMonthlyIncome($data['monthly_income'] ?? null, $error);
|
||||
if ($monthlyIncome === null) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => $error]);
|
||||
exit;
|
||||
}
|
||||
|
||||
$dependentsCount = normalizeDependentsCount($data['dependents_count'] ?? null, $error);
|
||||
if ($dependentsCount === null) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => $error]);
|
||||
exit;
|
||||
}
|
||||
|
||||
$housingConditionScore = normalizeHousingConditionScore($data['housing_condition_score'] ?? null, $error);
|
||||
if ($housingConditionScore === null) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => $error]);
|
||||
exit;
|
||||
}
|
||||
|
||||
$employmentStatus = normalizeEmploymentStatus($data['employment_status'] ?? null, $error);
|
||||
if ($employmentStatus === null) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => $error]);
|
||||
exit;
|
||||
}
|
||||
|
||||
$economicStatus = isset($data['economic_status']) ? strtolower(trim((string)$data['economic_status'])) : 'unknown';
|
||||
$allowedEconomicStatuses = ['sangat_miskin', 'miskin', 'rentan', 'cukup', 'baik', 'unknown'];
|
||||
if (!in_array($economicStatus, $allowedEconomicStatuses, true)) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'economic_status is invalid']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$verificationStatus = normalizeVerificationStatus($data['verification_status'] ?? null, $error, true);
|
||||
if ($verificationStatus === null) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => $error]);
|
||||
exit;
|
||||
}
|
||||
|
||||
$needPointId = null;
|
||||
if (array_key_exists('need_point_id', $data) && $data['need_point_id'] !== null && $data['need_point_id'] !== '') {
|
||||
if (!is_numeric($data['need_point_id'])) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'need_point_id must be numeric']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$needPointId = (int)$data['need_point_id'];
|
||||
$needPointStmt = $db->prepare('SELECT id, masjid_id FROM need_points WHERE id = ?');
|
||||
$needPointStmt->execute([$needPointId]);
|
||||
$needPoint = $needPointStmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
if (!$needPoint) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'need_point_id is not found']);
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
$masjidId = null;
|
||||
if (array_key_exists('masjid_id', $data) && $data['masjid_id'] !== null && $data['masjid_id'] !== '') {
|
||||
if (!is_numeric($data['masjid_id'])) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'masjid_id must be numeric']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$masjidId = (int)$data['masjid_id'];
|
||||
$masjidStmt = $db->prepare('SELECT id FROM masjids WHERE id = ?');
|
||||
$masjidStmt->execute([$masjidId]);
|
||||
if (!$masjidStmt->fetch(PDO::FETCH_ASSOC)) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'masjid_id is not found']);
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
if ($masjidId === null) {
|
||||
if ($needPointId !== null) {
|
||||
$npMasjidStmt = $db->prepare('SELECT masjid_id FROM need_points WHERE id = ?');
|
||||
$npMasjidStmt->execute([$needPointId]);
|
||||
$npMasjid = $npMasjidStmt->fetch(PDO::FETCH_ASSOC);
|
||||
if ($npMasjid && isset($npMasjid['masjid_id'])) {
|
||||
$masjidId = (int)$npMasjid['masjid_id'];
|
||||
}
|
||||
}
|
||||
|
||||
if ($masjidId === null) {
|
||||
$nearestMasjid = findNearestMasjidCoveringPoint($db, $latitude, $longitude);
|
||||
if ($nearestMasjid) {
|
||||
$masjidId = (int)$nearestMasjid['id'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$pontianakAreaId = null;
|
||||
if (array_key_exists('pontianak_area_id', $data) && $data['pontianak_area_id'] !== null && $data['pontianak_area_id'] !== '') {
|
||||
if (!is_numeric($data['pontianak_area_id'])) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'pontianak_area_id must be numeric']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$pontianakAreaId = (int)$data['pontianak_area_id'];
|
||||
$areaStmt = $db->prepare('SELECT id FROM pontianak_areas WHERE id = ?');
|
||||
$areaStmt->execute([$pontianakAreaId]);
|
||||
if (!$areaStmt->fetch(PDO::FETCH_ASSOC)) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'pontianak_area_id is not found']);
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
$householdCode = normalizeHouseholdCode($db, $data['household_code'] ?? null);
|
||||
|
||||
$nikHash = null;
|
||||
if (array_key_exists('nik', $data)) {
|
||||
$nikHash = normalizeNikHashFromInput($data['nik']);
|
||||
} else if (array_key_exists('nik_hash', $data)) {
|
||||
$nikHash = trim((string)$data['nik_hash']);
|
||||
if ($nikHash === '') {
|
||||
$nikHash = null;
|
||||
}
|
||||
}
|
||||
|
||||
$vulnerabilityNotes = isset($data['vulnerability_notes']) ? trim((string)$data['vulnerability_notes']) : null;
|
||||
if ($vulnerabilityNotes === '') {
|
||||
$vulnerabilityNotes = null;
|
||||
}
|
||||
|
||||
$insertStmt = $db->prepare('
|
||||
INSERT INTO households (
|
||||
household_code,
|
||||
head_name,
|
||||
nik_hash,
|
||||
latitude,
|
||||
longitude,
|
||||
need_point_id,
|
||||
masjid_id,
|
||||
pontianak_area_id,
|
||||
monthly_income,
|
||||
dependents_count,
|
||||
housing_condition_score,
|
||||
employment_status,
|
||||
economic_status,
|
||||
verification_status,
|
||||
vulnerability_notes
|
||||
)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
');
|
||||
|
||||
$insertStmt->execute([
|
||||
$householdCode,
|
||||
$headName,
|
||||
$nikHash,
|
||||
$latitude,
|
||||
$longitude,
|
||||
$needPointId,
|
||||
$masjidId,
|
||||
$pontianakAreaId,
|
||||
$monthlyIncome,
|
||||
$dependentsCount,
|
||||
$housingConditionScore,
|
||||
$employmentStatus,
|
||||
$economicStatus,
|
||||
$verificationStatus,
|
||||
$vulnerabilityNotes
|
||||
]);
|
||||
|
||||
$id = (int)$db->lastInsertId();
|
||||
$household = getHouseholdById($db, $id);
|
||||
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'household' => $household
|
||||
]);
|
||||
} catch (PDOException $e) {
|
||||
if ($e->getCode() === '23000') {
|
||||
http_response_code(409);
|
||||
echo json_encode(['error' => 'Duplicate household_code or need_point_id mapping']);
|
||||
exit;
|
||||
}
|
||||
|
||||
http_response_code(500);
|
||||
echo json_encode(['error' => $e->getMessage()]);
|
||||
} catch (Exception $e) {
|
||||
http_response_code(500);
|
||||
echo json_encode(['error' => $e->getMessage()]);
|
||||
}
|
||||
} else {
|
||||
http_response_code(405);
|
||||
echo json_encode(['error' => 'Method not allowed']);
|
||||
}
|
||||
?>
|
||||
@@ -0,0 +1,113 @@
|
||||
<?php
|
||||
error_reporting(E_ALL);
|
||||
ini_set('display_errors', 0);
|
||||
header('Content-Type: application/json');
|
||||
|
||||
require_once 'config.php';
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
try {
|
||||
$data = json_decode(file_get_contents('php://input'), true);
|
||||
|
||||
// Validate required fields
|
||||
if (!isset($data['name']) || !isset($data['road_type']) || !isset($data['coordinates']) || !isset($data['length_meters'])) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'Missing required fields: name, road_type, coordinates, or length_meters']);
|
||||
exit;
|
||||
}
|
||||
|
||||
// Validate road_type
|
||||
$validTypes = ['Nasional', 'Provinsi', 'Kabupaten'];
|
||||
if (!in_array($data['road_type'], $validTypes)) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'Invalid road_type. Must be: Nasional, Provinsi, or Kabupaten']);
|
||||
exit;
|
||||
}
|
||||
|
||||
// Validate coordinates array
|
||||
if (!is_array($data['coordinates']) || count($data['coordinates']) < 2) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'Coordinates must be an array with at least 2 points']);
|
||||
exit;
|
||||
}
|
||||
|
||||
// Validate each coordinate
|
||||
foreach ($data['coordinates'] as $coord) {
|
||||
if (!is_array($coord) || count($coord) !== 2) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'Each coordinate must be [latitude, longitude]']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$lat = floatval($coord[0]);
|
||||
$lng = floatval($coord[1]);
|
||||
|
||||
if (!is_numeric($coord[0]) || !is_numeric($coord[1])) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'Coordinates must be numeric values']);
|
||||
exit;
|
||||
}
|
||||
|
||||
if ($lat < -90 || $lat > 90 || $lng < -180 || $lng > 180) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'Invalid coordinate range. Latitude: -90 to 90, Longitude: -180 to 180']);
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
// Validate name is not empty
|
||||
if (empty(trim($data['name']))) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'Road name cannot be empty']);
|
||||
exit;
|
||||
}
|
||||
|
||||
// Validate length_meters is positive
|
||||
if (!is_numeric($data['length_meters']) || $data['length_meters'] <= 0) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'Length must be a positive number']);
|
||||
exit;
|
||||
}
|
||||
|
||||
// Validate condition_status if provided
|
||||
$condition_status = isset($data['condition_status']) ? $data['condition_status'] : 'normal';
|
||||
$validConditions = ['normal', 'minor_damage', 'major_damage'];
|
||||
if (!in_array($condition_status, $validConditions)) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'Invalid condition_status. Must be: normal, minor_damage, or major_damage']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$db = getDB();
|
||||
|
||||
// Store coordinates as JSON
|
||||
$coordinatesJson = json_encode($data['coordinates']);
|
||||
|
||||
$stmt = $db->prepare('INSERT INTO roads (name, road_type, coordinates, length_meters, condition_status) VALUES (?, ?, ?, ?, ?)');
|
||||
$stmt->execute([
|
||||
$data['name'],
|
||||
$data['road_type'],
|
||||
$coordinatesJson,
|
||||
floatval($data['length_meters']),
|
||||
$condition_status
|
||||
]);
|
||||
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'id' => $db->lastInsertId(),
|
||||
'name' => $data['name'],
|
||||
'road_type' => $data['road_type'],
|
||||
'coordinates' => $data['coordinates'],
|
||||
'length_meters' => floatval($data['length_meters']),
|
||||
'condition_status' => $condition_status,
|
||||
'created_at' => date('Y-m-d H:i:s')
|
||||
]);
|
||||
} catch (Exception $e) {
|
||||
http_response_code(500);
|
||||
echo json_encode(['error' => $e->getMessage()]);
|
||||
}
|
||||
} else {
|
||||
http_response_code(405);
|
||||
echo json_encode(['error' => 'Method not allowed']);
|
||||
}
|
||||
?>
|
||||
@@ -0,0 +1,76 @@
|
||||
<?php
|
||||
error_reporting(E_ALL);
|
||||
ini_set('display_errors', 0);
|
||||
header('Content-Type: application/json');
|
||||
|
||||
require_once 'config.php';
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
try {
|
||||
$data = json_decode(file_get_contents('php://input'), true);
|
||||
|
||||
if (!isset($data['name']) || !isset($data['latitude']) || !isset($data['longitude'])) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'Missing required fields: name, latitude, or longitude']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$name = trim($data['name']);
|
||||
if ($name === '') {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'Name cannot be empty']);
|
||||
exit;
|
||||
}
|
||||
|
||||
if (!is_numeric($data['latitude']) || !is_numeric($data['longitude'])) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'Latitude and longitude must be numeric values']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$latitude = (float)$data['latitude'];
|
||||
$longitude = (float)$data['longitude'];
|
||||
|
||||
if ($latitude < -90 || $latitude > 90) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'Latitude must be between -90 and 90']);
|
||||
exit;
|
||||
}
|
||||
|
||||
if ($longitude < -180 || $longitude > 180) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'Longitude must be between -180 and 180']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$radiusError = null;
|
||||
$radius = normalizeMasjidRadius(isset($data['radius_meters']) ? $data['radius_meters'] : null, $radiusError);
|
||||
if ($radius === null) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => $radiusError]);
|
||||
exit;
|
||||
}
|
||||
|
||||
$db = getDB();
|
||||
$stmt = $db->prepare('INSERT INTO masjids (name, latitude, longitude, radius_meters) VALUES (?, ?, ?, ?)');
|
||||
$stmt->execute([$name, $latitude, $longitude, $radius]);
|
||||
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'masjid' => [
|
||||
'id' => (int)$db->lastInsertId(),
|
||||
'name' => $name,
|
||||
'latitude' => $latitude,
|
||||
'longitude' => $longitude,
|
||||
'radius_meters' => $radius
|
||||
]
|
||||
]);
|
||||
} catch (Exception $e) {
|
||||
http_response_code(500);
|
||||
echo json_encode(['error' => $e->getMessage()]);
|
||||
}
|
||||
} else {
|
||||
http_response_code(405);
|
||||
echo json_encode(['error' => 'Method not allowed']);
|
||||
}
|
||||
?>
|
||||
@@ -0,0 +1,91 @@
|
||||
<?php
|
||||
error_reporting(E_ALL);
|
||||
ini_set('display_errors', 0);
|
||||
header('Content-Type: application/json');
|
||||
|
||||
require_once 'config.php';
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
try {
|
||||
$data = json_decode(file_get_contents('php://input'), true);
|
||||
|
||||
if (!isset($data['name']) || !isset($data['latitude']) || !isset($data['longitude'])) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'Missing required fields: name, latitude, or longitude']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$name = trim($data['name']);
|
||||
if ($name === '') {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'Name cannot be empty']);
|
||||
exit;
|
||||
}
|
||||
|
||||
if (!is_numeric($data['latitude']) || !is_numeric($data['longitude'])) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'Latitude and longitude must be numeric values']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$latitude = (float)$data['latitude'];
|
||||
$longitude = (float)$data['longitude'];
|
||||
|
||||
if ($latitude < -90 || $latitude > 90) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'Latitude must be between -90 and 90']);
|
||||
exit;
|
||||
}
|
||||
|
||||
if ($longitude < -180 || $longitude > 180) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'Longitude must be between -180 and 180']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$db = getDB();
|
||||
$nearestMasjid = findNearestMasjidCoveringPoint($db, $latitude, $longitude);
|
||||
|
||||
if ($nearestMasjid === null) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'Point must be inside at least one masjid radius']);
|
||||
exit;
|
||||
}
|
||||
|
||||
// optional household details
|
||||
$householdName = isset($data['household_name']) ? trim($data['household_name']) : null;
|
||||
$headName = isset($data['head_name']) ? trim($data['head_name']) : null;
|
||||
$economicStatus = isset($data['economic_status']) ? trim($data['economic_status']) : null;
|
||||
|
||||
// generate defaults when missing
|
||||
if (!$householdName) $householdName = 'Keluarga ' . substr(md5(uniqid('', true)), 0, 8);
|
||||
if (!$headName) {
|
||||
$firstNames = ['Ahmad','Budi','Siti','Aulia','Rizal','Dewi','Agus','Fitri','Hendra','Yulia'];
|
||||
$lastNames = ['Santoso','Wijaya','Pratama','Hidayat','Saputra','Nur','Ibrahim','Setiawan','Rahman','Putri'];
|
||||
$headName = $firstNames[array_rand($firstNames)] . ' ' . $lastNames[array_rand($lastNames)];
|
||||
}
|
||||
if (!$economicStatus) {
|
||||
$statuses = ['sangat_miskin','miskin','rentan','cukup','baik'];
|
||||
$economicStatus = $statuses[array_rand($statuses)];
|
||||
}
|
||||
|
||||
$insertStmt = $db->prepare('INSERT INTO need_points (name, latitude, longitude, masjid_id, household_name, head_name, economic_status) VALUES (?, ?, ?, ?, ?, ?, ?)');
|
||||
$insertStmt->execute([$name, $latitude, $longitude, (int)$nearestMasjid['id'], $householdName, $headName, $economicStatus]);
|
||||
|
||||
$pointId = (int)$db->lastInsertId();
|
||||
$point = getNeedPointWithMasjid($db, $pointId);
|
||||
$point['distance_meters'] = $nearestMasjid['distance_meters'];
|
||||
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'point' => $point
|
||||
]);
|
||||
} catch (Exception $e) {
|
||||
http_response_code(500);
|
||||
echo json_encode(['error' => $e->getMessage()]);
|
||||
}
|
||||
} else {
|
||||
http_response_code(405);
|
||||
echo json_encode(['error' => 'Method not allowed']);
|
||||
}
|
||||
?>
|
||||
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
error_reporting(E_ALL);
|
||||
ini_set('display_errors', 0); // Don't display errors in output
|
||||
header('Content-Type: application/json');
|
||||
|
||||
require_once 'config.php';
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
try {
|
||||
$data = json_decode(file_get_contents('php://input'), true);
|
||||
|
||||
// Validate required fields
|
||||
if (!isset($data['name']) || !isset($data['latitude']) || !isset($data['longitude'])) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'Missing required fields: name, latitude, or longitude']);
|
||||
exit;
|
||||
}
|
||||
|
||||
// Validate coordinates are numeric
|
||||
if (!is_numeric($data['latitude']) || !is_numeric($data['longitude'])) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'Latitude and longitude must be numeric values']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$db = getDB();
|
||||
$stmt = $db->prepare('INSERT INTO markers (name, nomor_spbu, latitude, longitude, open_24_hours) VALUES (?, ?, ?, ?, ?)');
|
||||
$open_24_hours = isset($data['open_24_hours']) ? (int)$data['open_24_hours'] : 0;
|
||||
$nomor_spbu = isset($data['nomor_spbu']) ? $data['nomor_spbu'] : null;
|
||||
|
||||
$stmt->execute([
|
||||
$data['name'],
|
||||
$nomor_spbu,
|
||||
(float)$data['latitude'],
|
||||
(float)$data['longitude'],
|
||||
$open_24_hours
|
||||
]);
|
||||
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'id' => $db->lastInsertId(),
|
||||
'name' => $data['name'],
|
||||
'nomor_spbu' => $nomor_spbu,
|
||||
'latitude' => (float)$data['latitude'],
|
||||
'longitude' => (float)$data['longitude'],
|
||||
'open_24_hours' => $open_24_hours
|
||||
]);
|
||||
} catch (Exception $e) {
|
||||
http_response_code(500);
|
||||
echo json_encode(['error' => $e->getMessage()]);
|
||||
}
|
||||
} else {
|
||||
http_response_code(405);
|
||||
echo json_encode(['error' => 'Method not allowed']);
|
||||
}
|
||||
?>
|
||||
@@ -0,0 +1,102 @@
|
||||
<?php
|
||||
error_reporting(E_ALL);
|
||||
ini_set('display_errors', 0);
|
||||
header('Content-Type: application/json');
|
||||
|
||||
require_once 'config.php';
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
try {
|
||||
$data = json_decode(file_get_contents('php://input'), true);
|
||||
|
||||
// Validate required fields
|
||||
if (!isset($data['name']) || !isset($data['certificate_type']) || !isset($data['coordinates']) || !isset($data['area_sqm'])) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'Missing required fields: name, certificate_type, coordinates, or area_sqm']);
|
||||
exit;
|
||||
}
|
||||
|
||||
// Validate certificate_type
|
||||
$validTypes = ['SHM', 'HGB', 'HGU', 'HP'];
|
||||
if (!in_array($data['certificate_type'], $validTypes)) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'Invalid certificate_type. Must be: SHM, HGB, HGU, or HP']);
|
||||
exit;
|
||||
}
|
||||
|
||||
// Validate coordinates array (minimum 3 for polygon)
|
||||
if (!is_array($data['coordinates']) || count($data['coordinates']) < 3) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'Coordinates must be an array with at least 3 points for a polygon']);
|
||||
exit;
|
||||
}
|
||||
|
||||
// Validate each coordinate
|
||||
foreach ($data['coordinates'] as $coord) {
|
||||
if (!is_array($coord) || count($coord) !== 2) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'Each coordinate must be [latitude, longitude]']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$lat = floatval($coord[0]);
|
||||
$lng = floatval($coord[1]);
|
||||
|
||||
if (!is_numeric($coord[0]) || !is_numeric($coord[1])) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'Coordinates must be numeric values']);
|
||||
exit;
|
||||
}
|
||||
|
||||
if ($lat < -90 || $lat > 90 || $lng < -180 || $lng > 180) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'Invalid coordinate range. Latitude: -90 to 90, Longitude: -180 to 180']);
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
// Validate name is not empty
|
||||
if (empty(trim($data['name']))) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'Parcel name cannot be empty']);
|
||||
exit;
|
||||
}
|
||||
|
||||
// Validate area_sqm is positive
|
||||
if (!is_numeric($data['area_sqm']) || $data['area_sqm'] <= 0) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'Area must be a positive number']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$db = getDB();
|
||||
|
||||
// Store coordinates as JSON
|
||||
$coordinatesJson = json_encode($data['coordinates']);
|
||||
|
||||
$stmt = $db->prepare('INSERT INTO land_parcels (name, certificate_type, coordinates, area_sqm) VALUES (?, ?, ?, ?)');
|
||||
$stmt->execute([
|
||||
$data['name'],
|
||||
$data['certificate_type'],
|
||||
$coordinatesJson,
|
||||
floatval($data['area_sqm'])
|
||||
]);
|
||||
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'id' => $db->lastInsertId(),
|
||||
'name' => $data['name'],
|
||||
'certificate_type' => $data['certificate_type'],
|
||||
'coordinates' => $data['coordinates'],
|
||||
'area_sqm' => floatval($data['area_sqm']),
|
||||
'created_at' => date('Y-m-d H:i:s')
|
||||
]);
|
||||
} catch (Exception $e) {
|
||||
http_response_code(500);
|
||||
echo json_encode(['error' => $e->getMessage()]);
|
||||
}
|
||||
} else {
|
||||
http_response_code(405);
|
||||
echo json_encode(['error' => 'Method not allowed']);
|
||||
}
|
||||
?>
|
||||
@@ -0,0 +1,138 @@
|
||||
<?php
|
||||
error_reporting(E_ALL);
|
||||
ini_set('display_errors', 0);
|
||||
header('Content-Type: application/json');
|
||||
|
||||
require_once 'config.php';
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
try {
|
||||
$data = json_decode(file_get_contents('php://input'), true);
|
||||
|
||||
if (!is_array($data)) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'Invalid JSON body']);
|
||||
exit;
|
||||
}
|
||||
|
||||
if (!isset($data['household_id']) || !is_numeric($data['household_id'])) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'Missing or invalid household_id']);
|
||||
exit;
|
||||
}
|
||||
|
||||
if (!isset($data['verifier_name']) || trim((string)$data['verifier_name']) === '') {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'Missing required field: verifier_name']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$error = null;
|
||||
$verificationStatus = normalizeVerificationStatus($data['verification_status'] ?? null, $error, false);
|
||||
if ($verificationStatus === null) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => $error]);
|
||||
exit;
|
||||
}
|
||||
|
||||
$evidencePhotoCount = normalizeEvidencePhotoCount($data['evidence_photo_count'] ?? null, $error);
|
||||
if ($evidencePhotoCount === null) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => $error]);
|
||||
exit;
|
||||
}
|
||||
|
||||
$dataConfidenceScore = normalizeDataConfidenceScore($data['data_confidence_score'] ?? null, $error);
|
||||
if ($dataConfidenceScore === null && $error !== null) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => $error]);
|
||||
exit;
|
||||
}
|
||||
|
||||
$householdId = (int)$data['household_id'];
|
||||
$verifierName = trim((string)$data['verifier_name']);
|
||||
$verifierRole = isset($data['verifier_role']) ? trim((string)$data['verifier_role']) : null;
|
||||
if ($verifierRole === '') {
|
||||
$verifierRole = null;
|
||||
}
|
||||
|
||||
$fieldNote = isset($data['field_note']) ? trim((string)$data['field_note']) : null;
|
||||
if ($fieldNote === '') {
|
||||
$fieldNote = null;
|
||||
}
|
||||
|
||||
$verifiedAt = isset($data['verified_at']) && trim((string)$data['verified_at']) !== ''
|
||||
? trim((string)$data['verified_at'])
|
||||
: date('Y-m-d H:i:s');
|
||||
|
||||
$verifiedDateTime = DateTime::createFromFormat('Y-m-d H:i:s', $verifiedAt);
|
||||
if (!$verifiedDateTime || $verifiedDateTime->format('Y-m-d H:i:s') !== $verifiedAt) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'verified_at must be in YYYY-MM-DD HH:MM:SS format']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$db = getDB();
|
||||
if (!householdExists($db, $householdId)) {
|
||||
http_response_code(404);
|
||||
echo json_encode(['error' => 'Household not found']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$insertStmt = $db->prepare('
|
||||
INSERT INTO verification_logs (
|
||||
household_id,
|
||||
verification_status,
|
||||
verifier_name,
|
||||
verifier_role,
|
||||
field_note,
|
||||
evidence_photo_count,
|
||||
data_confidence_score,
|
||||
verified_at
|
||||
)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
||||
');
|
||||
|
||||
$insertStmt->execute([
|
||||
$householdId,
|
||||
$verificationStatus,
|
||||
$verifierName,
|
||||
$verifierRole,
|
||||
$fieldNote,
|
||||
$evidencePhotoCount,
|
||||
$dataConfidenceScore,
|
||||
$verifiedAt
|
||||
]);
|
||||
|
||||
$updateHouseholdStmt = $db->prepare('UPDATE households SET verification_status = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?');
|
||||
$updateHouseholdStmt->execute([$verificationStatus, $householdId]);
|
||||
|
||||
$id = (int)$db->lastInsertId();
|
||||
$resultStmt = $db->prepare('
|
||||
SELECT
|
||||
vl.*,
|
||||
h.household_code,
|
||||
h.head_name
|
||||
FROM verification_logs vl
|
||||
JOIN households h ON h.id = vl.household_id
|
||||
WHERE vl.id = ?
|
||||
');
|
||||
$resultStmt->execute([$id]);
|
||||
$verificationLog = $resultStmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
$household = getHouseholdById($db, $householdId);
|
||||
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'verification_log' => $verificationLog,
|
||||
'household' => $household
|
||||
]);
|
||||
} catch (Exception $e) {
|
||||
http_response_code(500);
|
||||
echo json_encode(['error' => $e->getMessage()]);
|
||||
}
|
||||
} else {
|
||||
http_response_code(405);
|
||||
echo json_encode(['error' => 'Method not allowed']);
|
||||
}
|
||||
?>
|
||||
@@ -0,0 +1,99 @@
|
||||
<?php
|
||||
header('Content-Type: application/json');
|
||||
require_once 'config.php';
|
||||
|
||||
try {
|
||||
$db = getDB();
|
||||
|
||||
// Get filter parameters
|
||||
$search = isset($_GET['search']) ? '%' . $_GET['search'] . '%' : '';
|
||||
$from_date = isset($_GET['from_date']) ? $_GET['from_date'] : '';
|
||||
$to_date = isset($_GET['to_date']) ? $_GET['to_date'] : '';
|
||||
$certificate_type = isset($_GET['certificate_type']) ? $_GET['certificate_type'] : '';
|
||||
|
||||
// Build dynamic WHERE clause
|
||||
$where_clauses = [];
|
||||
$params = [];
|
||||
|
||||
if ($search !== '%' && $search !== '%') {
|
||||
$where_clauses[] = '(name LIKE ?)';
|
||||
$params[] = $search;
|
||||
}
|
||||
|
||||
if ($from_date) {
|
||||
$where_clauses[] = '(DATE(created_at) >= ?)';
|
||||
$params[] = $from_date;
|
||||
}
|
||||
|
||||
if ($to_date) {
|
||||
$where_clauses[] = '(DATE(created_at) <= ?)';
|
||||
$params[] = $to_date;
|
||||
}
|
||||
|
||||
if ($certificate_type) {
|
||||
$where_clauses[] = '(certificate_type = ?)';
|
||||
$params[] = $certificate_type;
|
||||
}
|
||||
|
||||
// Build SQL query
|
||||
$sql = 'SELECT id, name, certificate_type, coordinates, area_sqm, created_at, updated_at FROM land_parcels';
|
||||
|
||||
if (count($where_clauses) > 0) {
|
||||
$sql .= ' WHERE ' . implode(' AND ', $where_clauses);
|
||||
}
|
||||
|
||||
$sql .= ' ORDER BY created_at DESC';
|
||||
|
||||
// Execute query
|
||||
$stmt = $db->prepare($sql);
|
||||
$stmt->execute($params);
|
||||
$parcels = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
// Decode coordinates JSON for each parcel
|
||||
foreach ($parcels as &$parcel) {
|
||||
$parcel['coordinates'] = json_decode($parcel['coordinates'], true);
|
||||
}
|
||||
|
||||
// Count total for query
|
||||
$count_sql = 'SELECT COUNT(*) as total FROM land_parcels' . (count($where_clauses) > 0 ? ' WHERE ' . implode(' AND ', $where_clauses) : '');
|
||||
$count_stmt = $db->prepare($count_sql);
|
||||
$count_stmt->execute($params);
|
||||
$total = $count_stmt->fetch(PDO::FETCH_ASSOC)['total'];
|
||||
|
||||
// Prepare filters applied info
|
||||
$filters_applied = [];
|
||||
if ($search !== '%' && $search !== '%') {
|
||||
$filters_applied['search'] = str_replace('%', '', $search);
|
||||
}
|
||||
if ($from_date || $to_date) {
|
||||
$filters_applied['date_range'] = [];
|
||||
if ($from_date) $filters_applied['date_range'][] = $from_date;
|
||||
if ($to_date) $filters_applied['date_range'][] = $to_date;
|
||||
}
|
||||
if ($certificate_type) {
|
||||
$filters_applied['certificate_type'] = $certificate_type;
|
||||
}
|
||||
|
||||
// Return success response
|
||||
http_response_code(200);
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'polygons' => $parcels,
|
||||
'total' => $total,
|
||||
'filters_applied' => $filters_applied
|
||||
]);
|
||||
|
||||
} catch (PDOException $e) {
|
||||
http_response_code(500);
|
||||
echo json_encode([
|
||||
'success' => false,
|
||||
'error' => 'Database error: ' . $e->getMessage()
|
||||
]);
|
||||
} catch (Exception $e) {
|
||||
http_response_code(400);
|
||||
echo json_encode([
|
||||
'success' => false,
|
||||
'error' => $e->getMessage()
|
||||
]);
|
||||
}
|
||||
?>
|
||||
@@ -0,0 +1,99 @@
|
||||
<?php
|
||||
header('Content-Type: application/json');
|
||||
require_once 'config.php';
|
||||
|
||||
try {
|
||||
$db = getDB();
|
||||
|
||||
// Get filter parameters
|
||||
$search = isset($_GET['search']) ? '%' . $_GET['search'] . '%' : '';
|
||||
$from_date = isset($_GET['from_date']) ? $_GET['from_date'] : '';
|
||||
$to_date = isset($_GET['to_date']) ? $_GET['to_date'] : '';
|
||||
$condition_status = isset($_GET['condition_status']) ? $_GET['condition_status'] : '';
|
||||
|
||||
// Build dynamic WHERE clause
|
||||
$where_clauses = [];
|
||||
$params = [];
|
||||
|
||||
if ($search !== '%' && $search !== '%') {
|
||||
$where_clauses[] = '(name LIKE ?)';
|
||||
$params[] = $search;
|
||||
}
|
||||
|
||||
if ($from_date) {
|
||||
$where_clauses[] = '(DATE(created_at) >= ?)';
|
||||
$params[] = $from_date;
|
||||
}
|
||||
|
||||
if ($to_date) {
|
||||
$where_clauses[] = '(DATE(created_at) <= ?)';
|
||||
$params[] = $to_date;
|
||||
}
|
||||
|
||||
if ($condition_status) {
|
||||
$where_clauses[] = '(condition_status = ?)';
|
||||
$params[] = $condition_status;
|
||||
}
|
||||
|
||||
// Build SQL query
|
||||
$sql = 'SELECT id, name, road_type, coordinates, length_meters, condition_status, created_at, updated_at FROM roads';
|
||||
|
||||
if (count($where_clauses) > 0) {
|
||||
$sql .= ' WHERE ' . implode(' AND ', $where_clauses);
|
||||
}
|
||||
|
||||
$sql .= ' ORDER BY created_at DESC';
|
||||
|
||||
// Execute query
|
||||
$stmt = $db->prepare($sql);
|
||||
$stmt->execute($params);
|
||||
$roads = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
// Decode coordinates JSON for each road
|
||||
foreach ($roads as &$road) {
|
||||
$road['coordinates'] = json_decode($road['coordinates'], true);
|
||||
}
|
||||
|
||||
// Count total for unfiltered query
|
||||
$count_sql = 'SELECT COUNT(*) as total FROM roads' . (count($where_clauses) > 0 ? ' WHERE ' . implode(' AND ', $where_clauses) : '');
|
||||
$count_stmt = $db->prepare($count_sql);
|
||||
$count_stmt->execute($params);
|
||||
$total = $count_stmt->fetch(PDO::FETCH_ASSOC)['total'];
|
||||
|
||||
// Prepare filters applied info
|
||||
$filters_applied = [];
|
||||
if ($search !== '%' && $search !== '%') {
|
||||
$filters_applied['search'] = str_replace('%', '', $search);
|
||||
}
|
||||
if ($from_date || $to_date) {
|
||||
$filters_applied['date_range'] = [];
|
||||
if ($from_date) $filters_applied['date_range'][] = $from_date;
|
||||
if ($to_date) $filters_applied['date_range'][] = $to_date;
|
||||
}
|
||||
if ($condition_status) {
|
||||
$filters_applied['condition_status'] = $condition_status;
|
||||
}
|
||||
|
||||
// Return success response
|
||||
http_response_code(200);
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'lines' => $roads,
|
||||
'total' => $total,
|
||||
'filters_applied' => $filters_applied
|
||||
]);
|
||||
|
||||
} catch (PDOException $e) {
|
||||
http_response_code(500);
|
||||
echo json_encode([
|
||||
'success' => false,
|
||||
'error' => 'Database error: ' . $e->getMessage()
|
||||
]);
|
||||
} catch (Exception $e) {
|
||||
http_response_code(400);
|
||||
echo json_encode([
|
||||
'success' => false,
|
||||
'error' => $e->getMessage()
|
||||
]);
|
||||
}
|
||||
?>
|
||||
@@ -0,0 +1,136 @@
|
||||
<?php
|
||||
header('Content-Type: application/json');
|
||||
require_once 'config.php';
|
||||
|
||||
try {
|
||||
$db = getDB();
|
||||
|
||||
// Check if force parameter is set
|
||||
$force = isset($_POST['force']) || isset($_GET['force']);
|
||||
|
||||
// Pontianak administrative data (dummy but more realistic non-rectangular polygons)
|
||||
// GeoJSON coordinate order is [longitude, latitude].
|
||||
$pontianak_data = [
|
||||
[
|
||||
'name' => 'Pontianak',
|
||||
'area_type' => 'city',
|
||||
'population' => 690000,
|
||||
'geometry' => '{"type":"Polygon","coordinates":[[[109.320000,-0.005000],[109.330000,-0.004000],[109.340000,-0.005500],[109.345000,-0.008000],[109.348000,-0.012000],[109.350000,-0.016000],[109.348000,-0.020000],[109.340000,-0.023000],[109.330000,-0.024000],[109.320000,-0.023500],[109.310000,-0.022000],[109.305000,-0.019000],[109.303000,-0.016000],[109.302000,-0.012000],[109.305000,-0.008000],[109.310000,-0.006000],[109.318000,-0.004500],[109.325000,-0.003500],[109.329000,-0.004000],[109.320000,-0.005000]]]}'
|
||||
],
|
||||
[
|
||||
'name' => 'Pontianak Barat',
|
||||
'area_type' => 'kecamatan',
|
||||
'population' => 112000,
|
||||
'geometry' => '{"type":"Polygon","coordinates":[[[109.305000,-0.010000],[109.315000,-0.009500],[109.318000,-0.013000],[109.315000,-0.015000],[109.305000,-0.016000],[109.302000,-0.013000],[109.305000,-0.010000]]]}'
|
||||
],
|
||||
[
|
||||
'name' => 'Pontianak Utara',
|
||||
'area_type' => 'kecamatan',
|
||||
'population' => 126000,
|
||||
'geometry' => '{"type":"Polygon","coordinates":[[[109.318000,-0.005000],[109.327000,-0.004500],[109.330000,-0.007000],[109.328000,-0.009500],[109.320000,-0.010000],[109.315000,-0.007500],[109.318000,-0.005000]]]}'
|
||||
],
|
||||
[
|
||||
'name' => 'Pontianak Kota',
|
||||
'area_type' => 'kecamatan',
|
||||
'population' => 148000,
|
||||
'geometry' => '{"type":"Polygon","coordinates":[[[109.320000,-0.010000],[109.327000,-0.009000],[109.330000,-0.011000],[109.328000,-0.015000],[109.320000,-0.015500],[109.315000,-0.013000],[109.320000,-0.010000]]]}'
|
||||
],
|
||||
[
|
||||
'name' => 'Pontianak Timur',
|
||||
'area_type' => 'kecamatan',
|
||||
'population' => 94000,
|
||||
'geometry' => '{"type":"Polygon","coordinates":[[[109.328000,-0.009500],[109.340000,-0.010000],[109.345000,-0.012500],[109.338000,-0.016000],[109.328000,-0.015000],[109.328000,-0.009500]]]}'
|
||||
],
|
||||
[
|
||||
'name' => 'Pontianak Selatan',
|
||||
'area_type' => 'kecamatan',
|
||||
'population' => 132000,
|
||||
'geometry' => '{"type":"Polygon","coordinates":[[[109.315000,-0.015000],[109.328000,-0.014500],[109.330000,-0.018000],[109.318000,-0.021000],[109.308000,-0.020000],[109.315000,-0.015000]]]}'
|
||||
],
|
||||
[
|
||||
'name' => 'Pontianak Tenggara',
|
||||
'area_type' => 'kecamatan',
|
||||
'population' => 91000,
|
||||
'geometry' => '{"type":"Polygon","coordinates":[[[109.328000,-0.015000],[109.338000,-0.016000],[109.340000,-0.019000],[109.330000,-0.021000],[109.320000,-0.020000],[109.328000,-0.015000]]]}'
|
||||
]
|
||||
];
|
||||
|
||||
// Check if data already exists
|
||||
$stmt = $db->prepare('SELECT COUNT(*) as count FROM pontianak_areas');
|
||||
$stmt->execute();
|
||||
$count = $stmt->fetch(PDO::FETCH_ASSOC)['count'];
|
||||
|
||||
if ($count > 0 && !$force) {
|
||||
http_response_code(200);
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'message' => 'Pontianak data already exists. Use force=true to reseed.'
|
||||
]);
|
||||
exit;
|
||||
}
|
||||
|
||||
// Delete existing data if force is true
|
||||
if ($force && $count > 0) {
|
||||
$db->exec('DELETE FROM area_statistics');
|
||||
$db->exec('DELETE FROM pontianak_areas');
|
||||
}
|
||||
|
||||
// Insert Pontianak areas
|
||||
$stmt = $db->prepare('
|
||||
INSERT OR IGNORE INTO pontianak_areas (name, area_type, geometry, population)
|
||||
VALUES (?, ?, ?, ?)
|
||||
');
|
||||
|
||||
$today = date('Y-m-d');
|
||||
$stats_stmt = $db->prepare('
|
||||
INSERT OR IGNORE INTO area_statistics (pontianak_area_id, statistic_type, value, data_date)
|
||||
VALUES (?, ?, ?, ?)
|
||||
');
|
||||
|
||||
$inserted = 0;
|
||||
foreach ($pontianak_data as $data) {
|
||||
$stmt->execute([
|
||||
$data['name'],
|
||||
$data['area_type'],
|
||||
$data['geometry'],
|
||||
$data['population']
|
||||
]);
|
||||
$inserted++;
|
||||
}
|
||||
|
||||
// Insert initial area statistics
|
||||
$areas_stmt = $db->prepare('SELECT id, population FROM pontianak_areas');
|
||||
$areas_stmt->execute();
|
||||
$areas = $areas_stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
foreach ($areas as $area) {
|
||||
// Insert population density statistic
|
||||
$stats_stmt->execute([
|
||||
$area['id'],
|
||||
'population_density',
|
||||
(float)$area['population'],
|
||||
$today
|
||||
]);
|
||||
}
|
||||
|
||||
http_response_code(200);
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'message' => 'Pontianak data seeded successfully',
|
||||
'areas_inserted' => $inserted
|
||||
]);
|
||||
|
||||
} catch (PDOException $e) {
|
||||
http_response_code(500);
|
||||
echo json_encode([
|
||||
'success' => false,
|
||||
'error' => 'Database error: ' . $e->getMessage()
|
||||
]);
|
||||
} catch (Exception $e) {
|
||||
http_response_code(400);
|
||||
echo json_encode([
|
||||
'success' => false,
|
||||
'error' => $e->getMessage()
|
||||
]);
|
||||
}
|
||||
?>
|
||||
@@ -0,0 +1,256 @@
|
||||
<?php
|
||||
header('Content-Type: application/json');
|
||||
require_once 'config.php';
|
||||
|
||||
function randomFloat($min, $max) {
|
||||
return $min + (mt_rand() / mt_getrandmax()) * ($max - $min);
|
||||
}
|
||||
|
||||
function buildGridLocation($bbox, $index, $total) {
|
||||
$rows = max(1, (int)ceil(sqrt($total)));
|
||||
$cols = max(1, (int)ceil($total / $rows));
|
||||
|
||||
$row = intdiv($index, $cols);
|
||||
$col = $index % $cols;
|
||||
|
||||
$latStep = ($bbox['max_lat'] - $bbox['min_lat']) / $rows;
|
||||
$lngStep = ($bbox['max_lng'] - $bbox['min_lng']) / $cols;
|
||||
|
||||
$baseLat = $bbox['min_lat'] + ($row + 0.5) * $latStep;
|
||||
$baseLng = $bbox['min_lng'] + ($col + 0.5) * $lngStep;
|
||||
|
||||
$latMin = max($bbox['min_lat'], $baseLat - $latStep * 0.35);
|
||||
$latMax = min($bbox['max_lat'], $baseLat + $latStep * 0.35);
|
||||
$lngMin = max($bbox['min_lng'], $baseLng - $lngStep * 0.35);
|
||||
$lngMax = min($bbox['max_lng'], $baseLng + $lngStep * 0.35);
|
||||
|
||||
return [
|
||||
'latitude' => randomFloat($latMin, $latMax),
|
||||
'longitude' => randomFloat($lngMin, $lngMax)
|
||||
];
|
||||
}
|
||||
|
||||
function distanceSquared($lat1, $lng1, $lat2, $lng2) {
|
||||
$dLat = $lat1 - $lat2;
|
||||
$dLng = $lng1 - $lng2;
|
||||
return ($dLat * $dLat) + ($dLng * $dLng);
|
||||
}
|
||||
|
||||
function findNearestMasjidId($latitude, $longitude, $masjids) {
|
||||
$nearestId = null;
|
||||
$nearestDistance = null;
|
||||
|
||||
foreach ($masjids as $masjid) {
|
||||
$distance = distanceSquared($latitude, $longitude, $masjid['latitude'], $masjid['longitude']);
|
||||
if ($nearestDistance === null || $distance < $nearestDistance) {
|
||||
$nearestDistance = $distance;
|
||||
$nearestId = $masjid['id'];
|
||||
}
|
||||
}
|
||||
|
||||
return $nearestId;
|
||||
}
|
||||
|
||||
try {
|
||||
$db = getDB();
|
||||
$force = isset($_POST['force']) || isset($_GET['force']);
|
||||
|
||||
// Load GeoJSON land polygons to avoid placing points at sea
|
||||
$geojsonPath = __DIR__ . '/../export.geojson';
|
||||
$landPolygons = [];
|
||||
if (file_exists($geojsonPath)) {
|
||||
$gj = json_decode(file_get_contents($geojsonPath), true);
|
||||
if ($gj && isset($gj['features']) && is_array($gj['features'])) {
|
||||
foreach ($gj['features'] as $feature) {
|
||||
if (!isset($feature['geometry'])) continue;
|
||||
$geom = $feature['geometry'];
|
||||
if ($geom['type'] === 'Polygon') {
|
||||
$landPolygons[] = $geom['coordinates'];
|
||||
} elseif ($geom['type'] === 'MultiPolygon') {
|
||||
foreach ($geom['coordinates'] as $poly) $landPolygons[] = $poly;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function pointInRing($lat, $lng, $ring) {
|
||||
$inside = false;
|
||||
$j = count($ring) - 1;
|
||||
for ($i = 0; $i < count($ring); $i++) {
|
||||
$xi = $ring[$i][0]; $yi = $ring[$i][1];
|
||||
$xj = $ring[$j][0]; $yj = $ring[$j][1];
|
||||
$intersect = (($yi > $lat) != ($yj > $lat)) && ($lng < ($xj - $xi) * ($lat - $yi) / ($yj - $yi + 0.0) + $xi);
|
||||
if ($intersect) $inside = !$inside;
|
||||
$j = $i;
|
||||
}
|
||||
return $inside;
|
||||
}
|
||||
|
||||
function isPointOnLand($lat, $lng, $landPolygons) {
|
||||
if (empty($landPolygons)) return true; // if no polygons available, default to true
|
||||
foreach ($landPolygons as $poly) {
|
||||
// poly is array of rings; test outer ring first
|
||||
$outer = $poly[0];
|
||||
if (pointInRing($lat, $lng, $outer)) {
|
||||
// ensure not in hole
|
||||
$isInHole = false;
|
||||
for ($r = 1; $r < count($poly); $r++) {
|
||||
if (pointInRing($lat, $lng, $poly[$r])) {
|
||||
$isInHole = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!$isInHole) return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
$regional_data = [
|
||||
['region' => 'Jakarta', 'province' => 'DKI Jakarta', 'bbox' => ['min_lat' => -6.40, 'max_lat' => -5.90, 'min_lng' => 106.60, 'max_lng' => 106.95], 'masjids' => 15, 'need_points' => 2435],
|
||||
['region' => 'Bandung', 'province' => 'Jawa Barat', 'bbox' => ['min_lat' => -7.50, 'max_lat' => -5.90, 'min_lng' => 106.00, 'max_lng' => 108.10], 'masjids' => 10, 'need_points' => 1500],
|
||||
['region' => 'Bekasi', 'province' => 'Jawa Barat', 'bbox' => ['min_lat' => -7.20, 'max_lat' => -6.00, 'min_lng' => 106.50, 'max_lng' => 108.00], 'masjids' => 6, 'need_points' => 800],
|
||||
['region' => 'Semarang', 'province' => 'Jawa Tengah', 'bbox' => ['min_lat' => -8.10, 'max_lat' => -5.90, 'min_lng' => 108.30, 'max_lng' => 111.20], 'masjids' => 9, 'need_points' => 1400],
|
||||
['region' => 'Yogyakarta', 'province' => 'DI Yogyakarta', 'bbox' => ['min_lat' => -8.20, 'max_lat' => -7.30, 'min_lng' => 109.90, 'max_lng' => 110.90], 'masjids' => 7, 'need_points' => 900],
|
||||
['region' => 'Surabaya', 'province' => 'Jawa Timur', 'bbox' => ['min_lat' => -8.80, 'max_lat' => -6.80, 'min_lng' => 111.00, 'max_lng' => 114.50], 'masjids' => 12, 'need_points' => 1800],
|
||||
['region' => 'Malang', 'province' => 'Jawa Timur', 'bbox' => ['min_lat' => -8.60, 'max_lat' => -7.30, 'min_lng' => 110.80, 'max_lng' => 113.20], 'masjids' => 5, 'need_points' => 700],
|
||||
['region' => 'Medan', 'province' => 'Sumatera Utara', 'bbox' => ['min_lat' => 1.00, 'max_lat' => 4.50, 'min_lng' => 98.00, 'max_lng' => 100.50], 'masjids' => 11, 'need_points' => 1600],
|
||||
['region' => 'Palembang', 'province' => 'Sumatera Selatan', 'bbox' => ['min_lat' => -4.20, 'max_lat' => -2.00, 'min_lng' => 103.00, 'max_lng' => 106.10], 'masjids' => 8, 'need_points' => 1100],
|
||||
['region' => 'Pekanbaru', 'province' => 'Riau', 'bbox' => ['min_lat' => -0.60, 'max_lat' => 2.20, 'min_lng' => 100.00, 'max_lng' => 102.90], 'masjids' => 6, 'need_points' => 850],
|
||||
['region' => 'Jambi', 'province' => 'Jambi', 'bbox' => ['min_lat' => -2.30, 'max_lat' => 0.50, 'min_lng' => 101.00, 'max_lng' => 104.20], 'masjids' => 4, 'need_points' => 600],
|
||||
['region' => 'Padang', 'province' => 'Sumatera Barat', 'bbox' => ['min_lat' => -2.90, 'max_lat' => 1.00, 'min_lng' => 99.40, 'max_lng' => 101.80], 'masjids' => 5, 'need_points' => 700],
|
||||
['region' => 'Makassar', 'province' => 'Sulawesi Selatan', 'bbox' => ['min_lat' => -6.50, 'max_lat' => -2.50, 'min_lng' => 118.00, 'max_lng' => 121.80], 'masjids' => 9, 'need_points' => 1300],
|
||||
['region' => 'Manado', 'province' => 'Sulawesi Utara', 'bbox' => ['min_lat' => 0.80, 'max_lat' => 4.00, 'min_lng' => 123.00, 'max_lng' => 125.80], 'masjids' => 4, 'need_points' => 550],
|
||||
['region' => 'Kendari', 'province' => 'Sulawesi Tenggara', 'bbox' => ['min_lat' => -6.50, 'max_lat' => -2.00, 'min_lng' => 120.00, 'max_lng' => 124.50], 'masjids' => 3, 'need_points' => 400],
|
||||
['region' => 'Palu', 'province' => 'Sulawesi Tengah', 'bbox' => ['min_lat' => -3.00, 'max_lat' => 2.00, 'min_lng' => 118.00, 'max_lng' => 123.50], 'masjids' => 4, 'need_points' => 500],
|
||||
['region' => 'Banjarmasin', 'province' => 'Kalimantan Selatan', 'bbox' => ['min_lat' => -4.20, 'max_lat' => -1.00, 'min_lng' => 114.00, 'max_lng' => 116.80], 'masjids' => 7, 'need_points' => 1000],
|
||||
['region' => 'Pontianak', 'province' => 'Kalimantan Barat', 'bbox' => ['min_lat' => -3.50, 'max_lat' => 3.00, 'min_lng' => 108.00, 'max_lng' => 112.80], 'masjids' => 5, 'need_points' => 700],
|
||||
['region' => 'Samarinda', 'province' => 'Kalimantan Timur', 'bbox' => ['min_lat' => -2.80, 'max_lat' => 2.00, 'min_lng' => 115.00, 'max_lng' => 119.80], 'masjids' => 6, 'need_points' => 850],
|
||||
['region' => 'Palangkaraya', 'province' => 'Kalimantan Tengah', 'bbox' => ['min_lat' => -3.80, 'max_lat' => 1.20, 'min_lng' => 112.00, 'max_lng' => 116.50], 'masjids' => 3, 'need_points' => 450],
|
||||
['region' => 'Jayapura', 'province' => 'Papua', 'bbox' => ['min_lat' => -9.50, 'max_lat' => -1.00, 'min_lng' => 130.00, 'max_lng' => 141.50], 'masjids' => 4, 'need_points' => 700],
|
||||
['region' => 'Mataram', 'province' => 'Nusa Tenggara Barat', 'bbox' => ['min_lat' => -9.20, 'max_lat' => -7.00, 'min_lng' => 115.20, 'max_lng' => 119.00], 'masjids' => 3, 'need_points' => 450],
|
||||
['region' => 'Kupang', 'province' => 'Nusa Tenggara Timur', 'bbox' => ['min_lat' => -11.20, 'max_lat' => -8.00, 'min_lng' => 118.50, 'max_lng' => 125.50], 'masjids' => 2, 'need_points' => 300],
|
||||
['region' => 'Ambon', 'province' => 'Maluku', 'bbox' => ['min_lat' => -8.50, 'max_lat' => -1.00, 'min_lng' => 125.00, 'max_lng' => 134.50], 'masjids' => 2, 'need_points' => 250],
|
||||
['region' => 'Denpasar', 'province' => 'Bali', 'bbox' => ['min_lat' => -9.20, 'max_lat' => -8.00, 'min_lng' => 114.50, 'max_lng' => 115.80], 'masjids' => 1, 'need_points' => 100]
|
||||
];
|
||||
|
||||
$stmt = $db->prepare('SELECT COUNT(*) as count FROM masjids');
|
||||
$stmt->execute();
|
||||
$masjidCount = (int)$stmt->fetch(PDO::FETCH_ASSOC)['count'];
|
||||
|
||||
if ($masjidCount > 20 && !$force) {
|
||||
http_response_code(200);
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'message' => 'Regional poverty data already exists. Use force=true to reseed.',
|
||||
'current_masjid_count' => $masjidCount
|
||||
]);
|
||||
exit;
|
||||
}
|
||||
|
||||
if ($force && $masjidCount > 0) {
|
||||
$db->exec('DELETE FROM need_points');
|
||||
$db->exec('DELETE FROM masjids');
|
||||
}
|
||||
|
||||
$db->beginTransaction();
|
||||
|
||||
$masjidStmt = $db->prepare('INSERT INTO masjids (name, latitude, longitude, radius_meters) VALUES (?, ?, ?, ?)');
|
||||
$needPointStmt = $db->prepare('INSERT INTO need_points (name, latitude, longitude, masjid_id, household_name, head_name, economic_status) VALUES (?, ?, ?, ?, ?, ?, ?)');
|
||||
|
||||
$insertedMasjids = 0;
|
||||
$insertedNeedPoints = 0;
|
||||
|
||||
foreach ($regional_data as $region) {
|
||||
$provinceMasjids = [];
|
||||
|
||||
for ($i = 0; $i < $region['masjids']; $i++) {
|
||||
// ensure masjid sits on land (try a few attempts)
|
||||
$attempt = 0;
|
||||
do {
|
||||
$location = buildGridLocation($region['bbox'], $i, $region['masjids']);
|
||||
$attempt++;
|
||||
} while ($attempt < 8 && !isPointOnLand($location['latitude'], $location['longitude'], $landPolygons));
|
||||
$latSpan = $region['bbox']['max_lat'] - $region['bbox']['min_lat'];
|
||||
$lngSpan = $region['bbox']['max_lng'] - $region['bbox']['min_lng'];
|
||||
$coverageEstimate = (int)round((($latSpan + $lngSpan) * 111000) / max(1, $region['masjids']) * 0.55);
|
||||
$radiusMeters = max(20000, min(180000, $coverageEstimate + rand(3000, 12000)));
|
||||
|
||||
$masjidStmt->execute([
|
||||
'Masjid ' . $region['region'] . ' - ' . ($i + 1),
|
||||
$location['latitude'],
|
||||
$location['longitude'],
|
||||
$radiusMeters
|
||||
]);
|
||||
|
||||
$provinceMasjids[] = [
|
||||
'id' => (int)$db->lastInsertId(),
|
||||
'latitude' => $location['latitude'],
|
||||
'longitude' => $location['longitude']
|
||||
];
|
||||
|
||||
$insertedMasjids++;
|
||||
}
|
||||
|
||||
for ($i = 0; $i < $region['need_points']; $i++) {
|
||||
// ensure need point lands on land; allow several tries
|
||||
$attempt = 0;
|
||||
do {
|
||||
$pointLocation = buildGridLocation($region['bbox'], $i, $region['need_points']);
|
||||
$attempt++;
|
||||
} while ($attempt < 12 && !isPointOnLand($pointLocation['latitude'], $pointLocation['longitude'], $landPolygons));
|
||||
$nearestMasjidId = findNearestMasjidId($pointLocation['latitude'], $pointLocation['longitude'], $provinceMasjids);
|
||||
|
||||
// generate household and head names and economic status
|
||||
$hhName = 'Keluarga ' . $region['region'] . ' - ' . ($i + 1);
|
||||
$firstNames = ['Ahmad','Budi','Siti','Aulia','Rizal','Dewi','Agus','Fitri','Hendra','Yulia'];
|
||||
$lastNames = ['Santoso','Wijaya','Pratama','Hidayat','Saputra','Nur','Ibrahim','Setiawan','Rahman','Putri'];
|
||||
$headName = $firstNames[array_rand($firstNames)] . ' ' . $lastNames[array_rand($lastNames)];
|
||||
$statuses = ['sangat_miskin','miskin','rentan','cukup','baik'];
|
||||
$econ = $statuses[array_rand($statuses)];
|
||||
|
||||
$needPointStmt->execute([
|
||||
'Keluarga Miskin ' . $region['region'] . ' - ' . ($i + 1),
|
||||
$pointLocation['latitude'],
|
||||
$pointLocation['longitude'],
|
||||
$nearestMasjidId,
|
||||
$hhName,
|
||||
$headName,
|
||||
$econ
|
||||
]);
|
||||
|
||||
$insertedNeedPoints++;
|
||||
}
|
||||
}
|
||||
|
||||
$db->commit();
|
||||
|
||||
http_response_code(200);
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'message' => 'Regional poverty assistance data seeded successfully',
|
||||
'masjids_inserted' => $insertedMasjids,
|
||||
'need_points_inserted' => $insertedNeedPoints,
|
||||
'regions_covered' => count($regional_data),
|
||||
'total_need_points_capacity' => 21935
|
||||
]);
|
||||
} catch (PDOException $e) {
|
||||
if (isset($db) && $db->inTransaction()) {
|
||||
$db->rollBack();
|
||||
}
|
||||
http_response_code(500);
|
||||
echo json_encode([
|
||||
'success' => false,
|
||||
'error' => 'Database error: ' . $e->getMessage()
|
||||
]);
|
||||
} catch (Exception $e) {
|
||||
if (isset($db) && $db->inTransaction()) {
|
||||
$db->rollBack();
|
||||
}
|
||||
http_response_code(400);
|
||||
echo json_encode([
|
||||
'success' => false,
|
||||
'error' => $e->getMessage()
|
||||
]);
|
||||
}
|
||||
?>
|
||||
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
error_reporting(E_ALL);
|
||||
ini_set('display_errors', 0);
|
||||
header('Content-Type: application/json');
|
||||
|
||||
require_once 'config.php';
|
||||
|
||||
try {
|
||||
$db = getDB();
|
||||
$syncResult = syncNeedPointsToHouseholds($db);
|
||||
$computedScores = ensureHouseholdScoresForAllHouseholds($db);
|
||||
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'sync_result' => $syncResult,
|
||||
'computed_missing_scores' => $computedScores
|
||||
]);
|
||||
} catch (Exception $e) {
|
||||
http_response_code(500);
|
||||
echo json_encode([
|
||||
'success' => false,
|
||||
'error' => $e->getMessage()
|
||||
]);
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
error_reporting(E_ALL);
|
||||
ini_set('display_errors', 0);
|
||||
header('Content-Type: application/json');
|
||||
|
||||
require_once 'config.php';
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||||
http_response_code(405);
|
||||
echo json_encode(['error' => 'Method not allowed']);
|
||||
exit;
|
||||
}
|
||||
|
||||
try {
|
||||
$data = json_decode(file_get_contents('php://input'), true);
|
||||
if (!isset($data['id'])) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'Missing required field: id']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$id = (int)$data['id'];
|
||||
$db = getDB();
|
||||
|
||||
$existing = getNeedPointWithMasjid($db, $id);
|
||||
if (!$existing) {
|
||||
http_response_code(404);
|
||||
echo json_encode(['error' => 'Need point not found']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$current = (int)($existing['is_verified'] ?? 0);
|
||||
$next = $current === 1 ? 0 : 1;
|
||||
|
||||
$stmt = $db->prepare('UPDATE need_points SET is_verified = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?');
|
||||
$stmt->execute([$next, $id]);
|
||||
|
||||
$updated = getNeedPointWithMasjid($db, $id);
|
||||
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'point' => $updated
|
||||
]);
|
||||
} catch (Exception $e) {
|
||||
http_response_code(500);
|
||||
echo json_encode(['error' => $e->getMessage()]);
|
||||
}
|
||||
?>
|
||||
@@ -0,0 +1,269 @@
|
||||
<?php
|
||||
error_reporting(E_ALL);
|
||||
ini_set('display_errors', 0);
|
||||
header('Content-Type: application/json');
|
||||
|
||||
require_once 'config.php';
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
try {
|
||||
$data = json_decode(file_get_contents('php://input'), true);
|
||||
|
||||
if (!is_array($data) || !isset($data['id'])) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'Missing required field: id']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$id = (int)$data['id'];
|
||||
$db = getDB();
|
||||
|
||||
$existingStmt = $db->prepare('SELECT * FROM households WHERE id = ?');
|
||||
$existingStmt->execute([$id]);
|
||||
$existing = $existingStmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
if (!$existing) {
|
||||
http_response_code(404);
|
||||
echo json_encode(['error' => 'Household not found']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$updates = [];
|
||||
$params = [];
|
||||
$error = null;
|
||||
|
||||
if (array_key_exists('household_code', $data)) {
|
||||
$householdCode = trim((string)$data['household_code']);
|
||||
if ($householdCode === '') {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'household_code cannot be empty']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$updates[] = 'household_code = ?';
|
||||
$params[] = $householdCode;
|
||||
}
|
||||
|
||||
if (array_key_exists('head_name', $data)) {
|
||||
$headName = trim((string)$data['head_name']);
|
||||
if ($headName === '') {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'head_name cannot be empty']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$updates[] = 'head_name = ?';
|
||||
$params[] = $headName;
|
||||
}
|
||||
|
||||
if (array_key_exists('nik', $data) || array_key_exists('nik_hash', $data)) {
|
||||
if (array_key_exists('nik', $data)) {
|
||||
$nikHash = normalizeNikHashFromInput($data['nik']);
|
||||
} else {
|
||||
$nikHash = trim((string)$data['nik_hash']);
|
||||
if ($nikHash === '') {
|
||||
$nikHash = null;
|
||||
}
|
||||
}
|
||||
|
||||
$updates[] = 'nik_hash = ?';
|
||||
$params[] = $nikHash;
|
||||
}
|
||||
|
||||
$hasLatitude = array_key_exists('latitude', $data);
|
||||
$hasLongitude = array_key_exists('longitude', $data);
|
||||
if ($hasLatitude || $hasLongitude) {
|
||||
$latitude = $hasLatitude ? $data['latitude'] : $existing['latitude'];
|
||||
$longitude = $hasLongitude ? $data['longitude'] : $existing['longitude'];
|
||||
|
||||
if (!validateCoordinates($latitude, $longitude, $error)) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => $error]);
|
||||
exit;
|
||||
}
|
||||
|
||||
$updates[] = 'latitude = ?';
|
||||
$params[] = (float)$latitude;
|
||||
$updates[] = 'longitude = ?';
|
||||
$params[] = (float)$longitude;
|
||||
}
|
||||
|
||||
if (array_key_exists('need_point_id', $data)) {
|
||||
if ($data['need_point_id'] === null || $data['need_point_id'] === '') {
|
||||
$needPointId = null;
|
||||
} else {
|
||||
if (!is_numeric($data['need_point_id'])) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'need_point_id must be numeric']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$needPointId = (int)$data['need_point_id'];
|
||||
$npStmt = $db->prepare('SELECT id FROM need_points WHERE id = ?');
|
||||
$npStmt->execute([$needPointId]);
|
||||
if (!$npStmt->fetch(PDO::FETCH_ASSOC)) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'need_point_id is not found']);
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
$updates[] = 'need_point_id = ?';
|
||||
$params[] = $needPointId;
|
||||
}
|
||||
|
||||
if (array_key_exists('masjid_id', $data)) {
|
||||
if ($data['masjid_id'] === null || $data['masjid_id'] === '') {
|
||||
$masjidId = null;
|
||||
} else {
|
||||
if (!is_numeric($data['masjid_id'])) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'masjid_id must be numeric']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$masjidId = (int)$data['masjid_id'];
|
||||
$masjidStmt = $db->prepare('SELECT id FROM masjids WHERE id = ?');
|
||||
$masjidStmt->execute([$masjidId]);
|
||||
if (!$masjidStmt->fetch(PDO::FETCH_ASSOC)) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'masjid_id is not found']);
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
$updates[] = 'masjid_id = ?';
|
||||
$params[] = $masjidId;
|
||||
}
|
||||
|
||||
if (array_key_exists('pontianak_area_id', $data)) {
|
||||
if ($data['pontianak_area_id'] === null || $data['pontianak_area_id'] === '') {
|
||||
$areaId = null;
|
||||
} else {
|
||||
if (!is_numeric($data['pontianak_area_id'])) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'pontianak_area_id must be numeric']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$areaId = (int)$data['pontianak_area_id'];
|
||||
$areaStmt = $db->prepare('SELECT id FROM pontianak_areas WHERE id = ?');
|
||||
$areaStmt->execute([$areaId]);
|
||||
if (!$areaStmt->fetch(PDO::FETCH_ASSOC)) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'pontianak_area_id is not found']);
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
$updates[] = 'pontianak_area_id = ?';
|
||||
$params[] = $areaId;
|
||||
}
|
||||
|
||||
if (array_key_exists('monthly_income', $data)) {
|
||||
$monthlyIncome = normalizeMonthlyIncome($data['monthly_income'], $error);
|
||||
if ($monthlyIncome === null) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => $error]);
|
||||
exit;
|
||||
}
|
||||
|
||||
$updates[] = 'monthly_income = ?';
|
||||
$params[] = $monthlyIncome;
|
||||
}
|
||||
|
||||
if (array_key_exists('dependents_count', $data)) {
|
||||
$dependentsCount = normalizeDependentsCount($data['dependents_count'], $error);
|
||||
if ($dependentsCount === null) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => $error]);
|
||||
exit;
|
||||
}
|
||||
|
||||
$updates[] = 'dependents_count = ?';
|
||||
$params[] = $dependentsCount;
|
||||
}
|
||||
|
||||
if (array_key_exists('housing_condition_score', $data)) {
|
||||
$housingConditionScore = normalizeHousingConditionScore($data['housing_condition_score'], $error);
|
||||
if ($housingConditionScore === null) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => $error]);
|
||||
exit;
|
||||
}
|
||||
|
||||
$updates[] = 'housing_condition_score = ?';
|
||||
$params[] = $housingConditionScore;
|
||||
}
|
||||
|
||||
if (array_key_exists('employment_status', $data)) {
|
||||
$employmentStatus = normalizeEmploymentStatus($data['employment_status'], $error);
|
||||
if ($employmentStatus === null) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => $error]);
|
||||
exit;
|
||||
}
|
||||
|
||||
$updates[] = 'employment_status = ?';
|
||||
$params[] = $employmentStatus;
|
||||
}
|
||||
|
||||
if (array_key_exists('verification_status', $data)) {
|
||||
$verificationStatus = normalizeVerificationStatus($data['verification_status'], $error, true);
|
||||
if ($verificationStatus === null) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => $error]);
|
||||
exit;
|
||||
}
|
||||
|
||||
$updates[] = 'verification_status = ?';
|
||||
$params[] = $verificationStatus;
|
||||
}
|
||||
|
||||
if (array_key_exists('vulnerability_notes', $data)) {
|
||||
$vulnerabilityNotes = trim((string)$data['vulnerability_notes']);
|
||||
if ($vulnerabilityNotes === '') {
|
||||
$vulnerabilityNotes = null;
|
||||
}
|
||||
|
||||
$updates[] = 'vulnerability_notes = ?';
|
||||
$params[] = $vulnerabilityNotes;
|
||||
}
|
||||
|
||||
if (empty($updates)) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'No fields to update']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$updates[] = 'updated_at = CURRENT_TIMESTAMP';
|
||||
$params[] = $id;
|
||||
|
||||
$sql = 'UPDATE households SET ' . implode(', ', $updates) . ' WHERE id = ?';
|
||||
$updateStmt = $db->prepare($sql);
|
||||
$updateStmt->execute($params);
|
||||
|
||||
$household = getHouseholdById($db, $id);
|
||||
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'household' => $household
|
||||
]);
|
||||
} catch (PDOException $e) {
|
||||
if ($e->getCode() === '23000') {
|
||||
http_response_code(409);
|
||||
echo json_encode(['error' => 'Duplicate household_code or need_point_id mapping']);
|
||||
exit;
|
||||
}
|
||||
|
||||
http_response_code(500);
|
||||
echo json_encode(['error' => $e->getMessage()]);
|
||||
} catch (Exception $e) {
|
||||
http_response_code(500);
|
||||
echo json_encode(['error' => $e->getMessage()]);
|
||||
}
|
||||
} else {
|
||||
http_response_code(405);
|
||||
echo json_encode(['error' => 'Method not allowed']);
|
||||
}
|
||||
?>
|
||||
@@ -0,0 +1,143 @@
|
||||
<?php
|
||||
error_reporting(E_ALL);
|
||||
ini_set('display_errors', 0);
|
||||
header('Content-Type: application/json');
|
||||
|
||||
require_once 'config.php';
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
try {
|
||||
$data = json_decode(file_get_contents('php://input'), true);
|
||||
|
||||
// Validate required fields
|
||||
if (!isset($data['id'])) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'Missing required field: id']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$id = intval($data['id']);
|
||||
$db = getDB();
|
||||
|
||||
// Determine what type of update this is
|
||||
$hasCoordinates = isset($data['coordinates']) && isset($data['length_meters']);
|
||||
$hasName = isset($data['name']);
|
||||
$hasRoadType = isset($data['road_type']);
|
||||
$hasConditionStatus = isset($data['condition_status']);
|
||||
|
||||
// Full edit (multiple fields including name or road_type)
|
||||
if ($hasName || $hasRoadType || $hasCoordinates || $hasConditionStatus) {
|
||||
$updates = [];
|
||||
$params = [];
|
||||
|
||||
if ($hasName) {
|
||||
if (empty($data['name'])) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'Name cannot be empty']);
|
||||
exit;
|
||||
}
|
||||
$updates[] = 'name = ?';
|
||||
$params[] = $data['name'];
|
||||
}
|
||||
|
||||
if ($hasRoadType) {
|
||||
$validTypes = ['Nasional', 'Provinsi', 'Kabupaten'];
|
||||
if (!in_array($data['road_type'], $validTypes)) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'Invalid road_type. Must be: Nasional, Provinsi, or Kabupaten']);
|
||||
exit;
|
||||
}
|
||||
$updates[] = 'road_type = ?';
|
||||
$params[] = $data['road_type'];
|
||||
}
|
||||
|
||||
if ($hasConditionStatus) {
|
||||
$validConditions = ['normal', 'minor_damage', 'major_damage'];
|
||||
if (!in_array($data['condition_status'], $validConditions)) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'Invalid condition_status. Must be: normal, minor_damage, or major_damage']);
|
||||
exit;
|
||||
}
|
||||
$updates[] = 'condition_status = ?';
|
||||
$params[] = $data['condition_status'];
|
||||
}
|
||||
|
||||
if ($hasCoordinates) {
|
||||
// Validate coordinates array
|
||||
if (!is_array($data['coordinates']) || count($data['coordinates']) < 2) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'Coordinates must be an array with at least 2 points']);
|
||||
exit;
|
||||
}
|
||||
|
||||
// Validate each coordinate
|
||||
foreach ($data['coordinates'] as $coord) {
|
||||
if (!is_array($coord) || count($coord) !== 2 || !is_numeric($coord[0]) || !is_numeric($coord[1])) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'Invalid coordinate format']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$lat = floatval($coord[0]);
|
||||
$lng = floatval($coord[1]);
|
||||
|
||||
if ($lat < -90 || $lat > 90 || $lng < -180 || $lng > 180) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'Invalid coordinate range']);
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
// Validate length_meters is positive
|
||||
if (!is_numeric($data['length_meters']) || $data['length_meters'] <= 0) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'Length must be a positive number']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$updates[] = 'coordinates = ?';
|
||||
$updates[] = 'length_meters = ?';
|
||||
$params[] = json_encode($data['coordinates']);
|
||||
$params[] = floatval($data['length_meters']);
|
||||
}
|
||||
|
||||
if (empty($updates)) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'No fields to update']);
|
||||
exit;
|
||||
}
|
||||
|
||||
// Add updated_at and id to params
|
||||
$updates[] = 'updated_at = CURRENT_TIMESTAMP';
|
||||
$params[] = $id;
|
||||
|
||||
$sql = 'UPDATE roads SET ' . implode(', ', $updates) . ' WHERE id = ?';
|
||||
$stmt = $db->prepare($sql);
|
||||
$stmt->execute($params);
|
||||
|
||||
// Fetch updated record
|
||||
$getStmt = $db->prepare('SELECT * FROM roads WHERE id = ?');
|
||||
$getStmt->execute([$id]);
|
||||
$road = $getStmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
if ($road) {
|
||||
$road['coordinates'] = json_decode($road['coordinates'], true);
|
||||
}
|
||||
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'line' => $road
|
||||
]);
|
||||
} else {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'No fields to update']);
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
http_response_code(500);
|
||||
echo json_encode(['error' => $e->getMessage()]);
|
||||
}
|
||||
} else {
|
||||
http_response_code(405);
|
||||
echo json_encode(['error' => 'Method not allowed']);
|
||||
}
|
||||
?>
|
||||
@@ -0,0 +1,125 @@
|
||||
<?php
|
||||
error_reporting(E_ALL);
|
||||
ini_set('display_errors', 0);
|
||||
header('Content-Type: application/json');
|
||||
|
||||
require_once 'config.php';
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
try {
|
||||
$data = json_decode(file_get_contents('php://input'), true);
|
||||
|
||||
if (!isset($data['id'])) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'Missing required field: id']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$id = (int)$data['id'];
|
||||
$db = getDB();
|
||||
|
||||
$existingStmt = $db->prepare('SELECT id, name, latitude, longitude, radius_meters FROM masjids WHERE id = ?');
|
||||
$existingStmt->execute([$id]);
|
||||
$existing = $existingStmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
if (!$existing) {
|
||||
http_response_code(404);
|
||||
echo json_encode(['error' => 'Masjid not found']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$updates = [];
|
||||
$params = [];
|
||||
|
||||
if (isset($data['name'])) {
|
||||
$name = trim($data['name']);
|
||||
if ($name === '') {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'Name cannot be empty']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$updates[] = 'name = ?';
|
||||
$params[] = $name;
|
||||
}
|
||||
|
||||
$hasLatitude = array_key_exists('latitude', $data);
|
||||
$hasLongitude = array_key_exists('longitude', $data);
|
||||
if ($hasLatitude || $hasLongitude) {
|
||||
$latitude = $hasLatitude ? $data['latitude'] : $existing['latitude'];
|
||||
$longitude = $hasLongitude ? $data['longitude'] : $existing['longitude'];
|
||||
|
||||
if (!is_numeric($latitude) || !is_numeric($longitude)) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'Latitude and longitude must be numeric values']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$latitude = (float)$latitude;
|
||||
$longitude = (float)$longitude;
|
||||
|
||||
if ($latitude < -90 || $latitude > 90) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'Latitude must be between -90 and 90']);
|
||||
exit;
|
||||
}
|
||||
|
||||
if ($longitude < -180 || $longitude > 180) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'Longitude must be between -180 and 180']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$updates[] = 'latitude = ?';
|
||||
$updates[] = 'longitude = ?';
|
||||
$params[] = $latitude;
|
||||
$params[] = $longitude;
|
||||
}
|
||||
|
||||
if (array_key_exists('radius_meters', $data)) {
|
||||
$radiusError = null;
|
||||
$radius = normalizeMasjidRadius($data['radius_meters'], $radiusError);
|
||||
if ($radius === null) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => $radiusError]);
|
||||
exit;
|
||||
}
|
||||
|
||||
$updates[] = 'radius_meters = ?';
|
||||
$params[] = $radius;
|
||||
}
|
||||
|
||||
if (empty($updates)) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'No fields to update']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$updates[] = 'updated_at = CURRENT_TIMESTAMP';
|
||||
$params[] = $id;
|
||||
|
||||
$updateSql = 'UPDATE masjids SET ' . implode(', ', $updates) . ' WHERE id = ?';
|
||||
$updateStmt = $db->prepare($updateSql);
|
||||
$updateStmt->execute($params);
|
||||
|
||||
$resultStmt = $db->prepare('SELECT id, name, latitude, longitude, radius_meters, created_at, updated_at FROM masjids WHERE id = ?');
|
||||
$resultStmt->execute([$id]);
|
||||
$masjid = $resultStmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
$cleanupResult = cleanupNeedPointsOutsideMasjidCoverage($db, $id);
|
||||
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'masjid' => $masjid,
|
||||
'deleted_need_points' => $cleanupResult['deleted_count'],
|
||||
'deleted_need_point_ids' => $cleanupResult['deleted_ids']
|
||||
]);
|
||||
} catch (Exception $e) {
|
||||
http_response_code(500);
|
||||
echo json_encode(['error' => $e->getMessage()]);
|
||||
}
|
||||
} else {
|
||||
http_response_code(405);
|
||||
echo json_encode(['error' => 'Method not allowed']);
|
||||
}
|
||||
?>
|
||||
@@ -0,0 +1,102 @@
|
||||
<?php
|
||||
error_reporting(E_ALL);
|
||||
ini_set('display_errors', 0);
|
||||
header('Content-Type: application/json');
|
||||
|
||||
require_once 'config.php';
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
try {
|
||||
$data = json_decode(file_get_contents('php://input'), true);
|
||||
|
||||
if (!isset($data['id'])) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'Missing required field: id']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$id = (int)$data['id'];
|
||||
$db = getDB();
|
||||
|
||||
$existing = getNeedPointWithMasjid($db, $id);
|
||||
if (!$existing) {
|
||||
http_response_code(404);
|
||||
echo json_encode(['error' => 'Point berkebutuhan not found']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$newName = isset($data['name']) ? trim($data['name']) : $existing['name'];
|
||||
if ($newName === '') {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'Name cannot be empty']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$newLatitude = array_key_exists('latitude', $data) ? $data['latitude'] : $existing['latitude'];
|
||||
$newLongitude = array_key_exists('longitude', $data) ? $data['longitude'] : $existing['longitude'];
|
||||
|
||||
if (!is_numeric($newLatitude) || !is_numeric($newLongitude)) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'Latitude and longitude must be numeric values']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$newLatitude = (float)$newLatitude;
|
||||
$newLongitude = (float)$newLongitude;
|
||||
|
||||
if ($newLatitude < -90 || $newLatitude > 90) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'Latitude must be between -90 and 90']);
|
||||
exit;
|
||||
}
|
||||
|
||||
if ($newLongitude < -180 || $newLongitude > 180) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'Longitude must be between -180 and 180']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$nearestMasjid = findNearestMasjidCoveringPoint($db, $newLatitude, $newLongitude);
|
||||
if ($nearestMasjid === null) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'Point must remain inside at least one masjid radius']);
|
||||
exit;
|
||||
}
|
||||
|
||||
// allow updating household fields as well
|
||||
$newHouseholdName = array_key_exists('household_name', $data) ? trim($data['household_name']) : $existing['household_name'];
|
||||
$newHeadName = array_key_exists('head_name', $data) ? trim($data['head_name']) : $existing['head_name'];
|
||||
$newEconomic = array_key_exists('economic_status', $data) ? trim($data['economic_status']) : $existing['economic_status'];
|
||||
|
||||
$updateStmt = $db->prepare('
|
||||
UPDATE need_points
|
||||
SET name = ?, latitude = ?, longitude = ?, masjid_id = ?, household_name = ?, head_name = ?, economic_status = ?, updated_at = CURRENT_TIMESTAMP
|
||||
WHERE id = ?
|
||||
');
|
||||
$updateStmt->execute([
|
||||
$newName,
|
||||
$newLatitude,
|
||||
$newLongitude,
|
||||
(int)$nearestMasjid['id'],
|
||||
$newHouseholdName,
|
||||
$newHeadName,
|
||||
$newEconomic,
|
||||
$id
|
||||
]);
|
||||
|
||||
$updated = getNeedPointWithMasjid($db, $id);
|
||||
$updated['distance_meters'] = $nearestMasjid['distance_meters'];
|
||||
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'point' => $updated
|
||||
]);
|
||||
} catch (Exception $e) {
|
||||
http_response_code(500);
|
||||
echo json_encode(['error' => $e->getMessage()]);
|
||||
}
|
||||
} else {
|
||||
http_response_code(405);
|
||||
echo json_encode(['error' => 'Method not allowed']);
|
||||
}
|
||||
?>
|
||||
@@ -0,0 +1,154 @@
|
||||
<?php
|
||||
error_reporting(E_ALL);
|
||||
ini_set('display_errors', 0);
|
||||
header('Content-Type: application/json');
|
||||
|
||||
require_once 'config.php';
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
try {
|
||||
$data = json_decode(file_get_contents('php://input'), true);
|
||||
|
||||
// Validate required fields
|
||||
if (!isset($data['id'])) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'Missing required field: id']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$id = (int)$data['id'];
|
||||
$db = getDB();
|
||||
|
||||
// Determine what type of update this is
|
||||
$hasCoordinates = isset($data['latitude']) && isset($data['longitude']);
|
||||
$hasName = isset($data['name']);
|
||||
$hasNomorSpbu = isset($data['nomor_spbu']);
|
||||
$hasStatus = isset($data['open_24_hours']);
|
||||
|
||||
// Full edit (multiple fields including coordinates or name)
|
||||
if ($hasName || ($hasCoordinates && ($hasName || $hasNomorSpbu || $hasStatus))) {
|
||||
$updates = [];
|
||||
$params = [];
|
||||
|
||||
if ($hasName) {
|
||||
if (empty($data['name'])) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'Name cannot be empty']);
|
||||
exit;
|
||||
}
|
||||
$updates[] = 'name = ?';
|
||||
$params[] = $data['name'];
|
||||
}
|
||||
|
||||
if ($hasNomorSpbu) {
|
||||
$updates[] = 'nomor_spbu = ?';
|
||||
$params[] = $data['nomor_spbu'] ?: null;
|
||||
}
|
||||
|
||||
if ($hasCoordinates) {
|
||||
if (!is_numeric($data['latitude']) || !is_numeric($data['longitude'])) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'Latitude and longitude must be numeric values']);
|
||||
exit;
|
||||
}
|
||||
$latitude = (float)$data['latitude'];
|
||||
$longitude = (float)$data['longitude'];
|
||||
|
||||
if ($latitude < -90 || $latitude > 90 || $longitude < -180 || $longitude > 180) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'Invalid coordinates']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$updates[] = 'latitude = ?';
|
||||
$updates[] = 'longitude = ?';
|
||||
$params[] = $latitude;
|
||||
$params[] = $longitude;
|
||||
}
|
||||
|
||||
if ($hasStatus) {
|
||||
$updates[] = 'open_24_hours = ?';
|
||||
$params[] = (int)$data['open_24_hours'];
|
||||
}
|
||||
|
||||
if (empty($updates)) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'No fields to update']);
|
||||
exit;
|
||||
}
|
||||
|
||||
// Add id to params
|
||||
$params[] = $id;
|
||||
|
||||
$sql = 'UPDATE markers SET ' . implode(', ', $updates) . ' WHERE id = ?';
|
||||
$stmt = $db->prepare($sql);
|
||||
$stmt->execute($params);
|
||||
|
||||
// Fetch updated record
|
||||
$getStmt = $db->prepare('SELECT * FROM markers WHERE id = ?');
|
||||
$getStmt->execute([$id]);
|
||||
$marker = $getStmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'marker' => $marker
|
||||
]);
|
||||
}
|
||||
// Coordinates-only update (drag)
|
||||
else if ($hasCoordinates) {
|
||||
if (!is_numeric($data['latitude']) || !is_numeric($data['longitude'])) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'Latitude and longitude must be numeric values']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$latitude = (float)$data['latitude'];
|
||||
$longitude = (float)$data['longitude'];
|
||||
|
||||
if ($latitude < -90 || $latitude > 90) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'Latitude must be between -90 and 90']);
|
||||
exit;
|
||||
}
|
||||
|
||||
if ($longitude < -180 || $longitude > 180) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'Longitude must be between -180 and 180']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$stmt = $db->prepare('UPDATE markers SET latitude = ?, longitude = ? WHERE id = ?');
|
||||
$stmt->execute([$latitude, $longitude, $id]);
|
||||
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'id' => $id,
|
||||
'latitude' => $latitude,
|
||||
'longitude' => $longitude
|
||||
]);
|
||||
}
|
||||
// Status-only update (toggle)
|
||||
else if ($hasStatus) {
|
||||
$open_24_hours = (int)$data['open_24_hours'];
|
||||
$stmt = $db->prepare('UPDATE markers SET open_24_hours = ? WHERE id = ?');
|
||||
$stmt->execute([$open_24_hours, $id]);
|
||||
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'id' => $id,
|
||||
'open_24_hours' => $open_24_hours
|
||||
]);
|
||||
} else {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'No fields to update']);
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
http_response_code(500);
|
||||
echo json_encode(['error' => $e->getMessage()]);
|
||||
}
|
||||
} else {
|
||||
http_response_code(405);
|
||||
echo json_encode(['error' => 'Method not allowed']);
|
||||
}
|
||||
?>
|
||||
|
||||
@@ -0,0 +1,131 @@
|
||||
<?php
|
||||
error_reporting(E_ALL);
|
||||
ini_set('display_errors', 0);
|
||||
header('Content-Type: application/json');
|
||||
|
||||
require_once 'config.php';
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
try {
|
||||
$data = json_decode(file_get_contents('php://input'), true);
|
||||
|
||||
// Validate required fields
|
||||
if (!isset($data['id'])) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'Missing required field: id']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$id = intval($data['id']);
|
||||
$db = getDB();
|
||||
|
||||
// Determine what type of update this is
|
||||
$hasCoordinates = isset($data['coordinates']) && isset($data['area_sqm']);
|
||||
$hasName = isset($data['name']);
|
||||
$hasCertificateType = isset($data['certificate_type']);
|
||||
|
||||
// Full edit (multiple fields including name or certificate_type)
|
||||
if ($hasName || $hasCertificateType || $hasCoordinates) {
|
||||
$updates = [];
|
||||
$params = [];
|
||||
|
||||
if ($hasName) {
|
||||
if (empty($data['name'])) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'Name cannot be empty']);
|
||||
exit;
|
||||
}
|
||||
$updates[] = 'name = ?';
|
||||
$params[] = $data['name'];
|
||||
}
|
||||
|
||||
if ($hasCertificateType) {
|
||||
$validTypes = ['SHM', 'HGB', 'HGU', 'HP'];
|
||||
if (!in_array($data['certificate_type'], $validTypes)) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'Invalid certificate_type. Must be: SHM, HGB, HGU, or HP']);
|
||||
exit;
|
||||
}
|
||||
$updates[] = 'certificate_type = ?';
|
||||
$params[] = $data['certificate_type'];
|
||||
}
|
||||
|
||||
if ($hasCoordinates) {
|
||||
// Validate coordinates array (minimum 3 for polygon)
|
||||
if (!is_array($data['coordinates']) || count($data['coordinates']) < 3) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'Coordinates must be an array with at least 3 points']);
|
||||
exit;
|
||||
}
|
||||
|
||||
// Validate each coordinate
|
||||
foreach ($data['coordinates'] as $coord) {
|
||||
if (!is_array($coord) || count($coord) !== 2 || !is_numeric($coord[0]) || !is_numeric($coord[1])) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'Invalid coordinate format']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$lat = floatval($coord[0]);
|
||||
$lng = floatval($coord[1]);
|
||||
|
||||
if ($lat < -90 || $lat > 90 || $lng < -180 || $lng > 180) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'Invalid coordinate range']);
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
// Validate area_sqm is positive
|
||||
if (!is_numeric($data['area_sqm']) || $data['area_sqm'] <= 0) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'Area must be a positive number']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$updates[] = 'coordinates = ?';
|
||||
$updates[] = 'area_sqm = ?';
|
||||
$params[] = json_encode($data['coordinates']);
|
||||
$params[] = floatval($data['area_sqm']);
|
||||
}
|
||||
|
||||
if (empty($updates)) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'No fields to update']);
|
||||
exit;
|
||||
}
|
||||
|
||||
// Add updated_at and id to params
|
||||
$updates[] = 'updated_at = CURRENT_TIMESTAMP';
|
||||
$params[] = $id;
|
||||
|
||||
$sql = 'UPDATE land_parcels SET ' . implode(', ', $updates) . ' WHERE id = ?';
|
||||
$stmt = $db->prepare($sql);
|
||||
$stmt->execute($params);
|
||||
|
||||
// Fetch updated record
|
||||
$getStmt = $db->prepare('SELECT * FROM land_parcels WHERE id = ?');
|
||||
$getStmt->execute([$id]);
|
||||
$parcel = $getStmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
if ($parcel) {
|
||||
$parcel['coordinates'] = json_decode($parcel['coordinates'], true);
|
||||
}
|
||||
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'polygon' => $parcel
|
||||
]);
|
||||
} else {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'No fields to update']);
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
http_response_code(500);
|
||||
echo json_encode(['error' => $e->getMessage()]);
|
||||
}
|
||||
} else {
|
||||
http_response_code(405);
|
||||
echo json_encode(['error' => 'Method not allowed']);
|
||||
}
|
||||
?>
|
||||
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
header('Content-Type: application/json');
|
||||
require_once 'config.php';
|
||||
|
||||
// Check authentication and authorization
|
||||
requireRole(['Admin']);
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'PUT') {
|
||||
http_response_code(405);
|
||||
die(json_encode(['error' => 'Method not allowed']));
|
||||
}
|
||||
|
||||
$data = json_decode(file_get_contents('php://input'), true);
|
||||
|
||||
// Validate input
|
||||
if (!isset($data['id']) || !isset($data['role'])) {
|
||||
http_response_code(400);
|
||||
die(json_encode(['error' => 'Missing required fields: id, role']));
|
||||
}
|
||||
|
||||
// Validate role
|
||||
$validRoles = ['Admin', 'Petugas', 'Pimpinan'];
|
||||
if (!in_array($data['role'], $validRoles)) {
|
||||
http_response_code(400);
|
||||
die(json_encode(['error' => 'Invalid role. Must be Admin, Petugas, or Pimpinan']));
|
||||
}
|
||||
|
||||
try {
|
||||
$db = getDB();
|
||||
|
||||
$stmt = $db->prepare('
|
||||
UPDATE users
|
||||
SET role = ?, updated_at = CURRENT_TIMESTAMP
|
||||
WHERE id = ?
|
||||
');
|
||||
$stmt->execute([$data['role'], $data['id']]);
|
||||
|
||||
if ($stmt->rowCount() > 0) {
|
||||
http_response_code(200);
|
||||
die(json_encode([
|
||||
'success' => true,
|
||||
'message' => 'User role updated successfully'
|
||||
]));
|
||||
} else {
|
||||
http_response_code(404);
|
||||
die(json_encode(['error' => 'User not found']));
|
||||
}
|
||||
} catch (PDOException $e) {
|
||||
http_response_code(500);
|
||||
die(json_encode(['error' => 'Database error: ' . $e->getMessage()]));
|
||||
}
|
||||
Reference in New Issue
Block a user