mock backend
This commit is contained in:
parent
9470b78cdc
commit
31ce78aca9
@ -41,4 +41,79 @@ document.addEventListener('DOMContentLoaded', function () {
|
||||
].join('');
|
||||
alertContainer.append(wrapper);
|
||||
}
|
||||
|
||||
// --- Search Functionality ---
|
||||
const searchForm = document.getElementById('searchForm');
|
||||
if (searchForm) {
|
||||
const searchInput = document.getElementById('searchInput');
|
||||
const searchButton = document.getElementById('searchButton');
|
||||
const searchResultsSection = document.getElementById('searchResults');
|
||||
const resultsContainer = document.getElementById('resultsContainer');
|
||||
const noResults = document.getElementById('noResults');
|
||||
|
||||
searchForm.addEventListener('submit', function(e) {
|
||||
e.preventDefault();
|
||||
const query = searchInput.value.trim();
|
||||
|
||||
if (query === '') {
|
||||
return;
|
||||
}
|
||||
|
||||
// Show loading state
|
||||
searchButton.disabled = true;
|
||||
searchButton.innerHTML = '<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span> Searching...';
|
||||
resultsContainer.innerHTML = '';
|
||||
noResults.style.display = 'none';
|
||||
searchResultsSection.style.display = 'block';
|
||||
|
||||
fetch(`search.php?q=${encodeURIComponent(query)}`)
|
||||
.then(response => {
|
||||
if (!response.ok) {
|
||||
throw new Error('Network response was not ok');
|
||||
}
|
||||
return response.json();
|
||||
})
|
||||
.then(data => {
|
||||
if (data.length > 0) {
|
||||
displayResults(data);
|
||||
} else {
|
||||
noResults.style.display = 'block';
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Search error:', error);
|
||||
resultsContainer.innerHTML = '<p class="text-danger text-center">An error occurred while searching. Please try again.</p>';
|
||||
})
|
||||
.finally(() => {
|
||||
// Restore button state
|
||||
searchButton.disabled = false;
|
||||
searchButton.innerHTML = 'Search';
|
||||
});
|
||||
});
|
||||
|
||||
function displayResults(products) {
|
||||
products.forEach(product => {
|
||||
const pricesHtml = product.prices.map(p => `
|
||||
<li class="list-group-item d-flex justify-content-between align-items-center">
|
||||
${p.retailer}
|
||||
<span class="badge bg-primary rounded-pill">R ${parseFloat(p.price).toFixed(2)}</span>
|
||||
</li>
|
||||
`).join('');
|
||||
|
||||
const productCard = `
|
||||
<div class="col-md-6 col-lg-4">
|
||||
<div class="card h-100">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">${product.name}</h5>
|
||||
<ul class="list-group list-group-flush">
|
||||
${pricesHtml}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
resultsContainer.innerHTML += productCard;
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
70
db/migrate.php
Normal file
70
db/migrate.php
Normal file
@ -0,0 +1,70 @@
|
||||
<?php
|
||||
// Simple, idempotent migration and seeding script.
|
||||
|
||||
require_once __DIR__ . '/config.php';
|
||||
|
||||
function run_migrations(PDO $pdo): void
|
||||
{
|
||||
$migrationsDir = __DIR__ . '/migrations';
|
||||
if (!is_dir($migrationsDir)) {
|
||||
echo "Migrations directory not found.\n";
|
||||
return;
|
||||
}
|
||||
|
||||
$files = glob($migrationsDir . '/*.sql');
|
||||
sort($files);
|
||||
|
||||
foreach ($files as $file) {
|
||||
echo "Running migration: " . basename($file) . "...\n";
|
||||
$sql = file_get_contents($file);
|
||||
if ($sql === false) {
|
||||
echo "Failed to read migration file: " . basename($file) . "\n";
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
$pdo->exec($sql);
|
||||
echo "Success.\n";
|
||||
} catch (PDOException $e) {
|
||||
echo "Error running migration " . basename($file) . ": " . $e->getMessage() . "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function seed_data(PDO $pdo): void
|
||||
{
|
||||
$products = [
|
||||
['name' => 'Milk 1L', 'retailer' => 'Pick n Pay', 'price' => 25.99],
|
||||
['name' => 'Milk 1L', 'retailer' => 'Checkers', 'price' => 24.99],
|
||||
['name' => 'Bread (White)', 'retailer' => 'Pick n Pay', 'price' => 15.50],
|
||||
['name' => 'Bread (White)', 'retailer' => 'Checkers', 'price' => 14.99],
|
||||
['name' => 'Eggs (12 pack)', 'retailer' => 'Pick n Pay', 'price' => 32.00],
|
||||
['name' => 'Eggs (12 pack)', 'retailer' => 'Checkers', 'price' => 31.50],
|
||||
['name' => 'Cheese (250g)', 'retailer' => 'Pick n Pay', 'price' => 55.00],
|
||||
['name' => 'Cheese (250g)', 'retailer' => 'Checkers', 'price' => 52.99],
|
||||
];
|
||||
|
||||
$stmt = $pdo->prepare(
|
||||
'INSERT INTO products (name, retailer, price) VALUES (:name, :retailer, :price)
|
||||
ON DUPLICATE KEY UPDATE price = VALUES(price)'
|
||||
);
|
||||
|
||||
echo "\nSeeding data...\n";
|
||||
foreach ($products as $product) {
|
||||
try {
|
||||
$stmt->execute($product);
|
||||
echo "Seeded/Updated: " . $product['name'] . " (" . $product['retailer'] . ")\n";
|
||||
} catch (PDOException $e) {
|
||||
echo "Error seeding " . $product['name'] . ": " . $e->getMessage() . "\n";
|
||||
}
|
||||
}
|
||||
echo "Seeding complete.\n";
|
||||
}
|
||||
|
||||
try {
|
||||
$pdo = db();
|
||||
run_migrations($pdo);
|
||||
seed_data($pdo);
|
||||
echo "\nDatabase setup complete!\n";
|
||||
} catch (PDOException $e) {
|
||||
die("Database connection failed: " . $e->getMessage() . "\n");
|
||||
}
|
||||
8
db/migrations/001_create_products_table.sql
Normal file
8
db/migrations/001_create_products_table.sql
Normal file
@ -0,0 +1,8 @@
|
||||
CREATE TABLE IF NOT EXISTS products (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
retailer VARCHAR(255) NOT NULL,
|
||||
price DECIMAL(10, 2) NOT NULL,
|
||||
last_updated TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
UNIQUE KEY `product_retailer` (`name`,`retailer`)
|
||||
);
|
||||
22
index.php
22
index.php
@ -5,12 +5,28 @@
|
||||
<div class="container">
|
||||
<h1>PricePal SA</h1>
|
||||
<p>Your smart shopping companion for comparing prices across South African retailers.</p>
|
||||
<a href="#" class="btn btn-light btn-lg">
|
||||
<i class="bi bi-google-play"></i> Download on Google Play
|
||||
</a>
|
||||
<form id="searchForm" class="mt-4">
|
||||
<div class="input-group input-group-lg">
|
||||
<input type="search" id="searchInput" class="form-control" placeholder="Search for a product (e.g., Milk, Bread...)" aria-label="Search for a product">
|
||||
<button class="btn btn-primary" type="submit" id="searchButton">Search</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<!-- Search Results Section -->
|
||||
<section id="searchResults" class="section" style="display: none;">
|
||||
<div class="container">
|
||||
<div class="text-center mb-5">
|
||||
<h2>Search Results</h2>
|
||||
</div>
|
||||
<div id="resultsContainer" class="row g-4"></div>
|
||||
<div id="noResults" class="text-center" style="display: none;">
|
||||
<p class="lead">No products found matching your search.</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Features Section -->
|
||||
<section id="features" class="section">
|
||||
<div class="container">
|
||||
|
||||
45
search.php
Normal file
45
search.php
Normal file
@ -0,0 +1,45 @@
|
||||
<?php
|
||||
header('Content-Type: application/json');
|
||||
|
||||
require_once __DIR__ . '/db/config.php';
|
||||
|
||||
$query = $_GET['q'] ?? '';
|
||||
|
||||
if (empty($query)) {
|
||||
echo json_encode([]);
|
||||
exit;
|
||||
}
|
||||
|
||||
try {
|
||||
$pdo = db();
|
||||
|
||||
// Find all products matching the query
|
||||
$stmt = $pdo->prepare(
|
||||
'SELECT name, retailer, price FROM products WHERE name LIKE :query ORDER BY name, price'
|
||||
);
|
||||
|
||||
$stmt->execute(['query' => '%' . $query . '%']);
|
||||
$results = $stmt->fetchAll();
|
||||
|
||||
// Group prices by product name
|
||||
$products = [];
|
||||
foreach ($results as $row) {
|
||||
if (!isset($products[$row['name']])) {
|
||||
$products[$row['name']] = [
|
||||
'name' => $row['name'],
|
||||
'prices' => []
|
||||
];
|
||||
}
|
||||
$products[$row['name']]['prices'][] = [
|
||||
'retailer' => $row['retailer'],
|
||||
'price' => $row['price']
|
||||
];
|
||||
}
|
||||
|
||||
// Return as a simple array of products
|
||||
echo json_encode(array_values($products));
|
||||
|
||||
} catch (PDOException $e) {
|
||||
http_response_code(500);
|
||||
echo json_encode(['error' => 'Database error: ' . $e->getMessage()]);
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user