Compare commits
No commits in common. "ai-dev" and "master" have entirely different histories.
@ -1,132 +0,0 @@
|
|||||||
/* General & Typography */
|
|
||||||
body {
|
|
||||||
font-family: 'Inter', sans-serif;
|
|
||||||
background-color: #F9FAFB;
|
|
||||||
color: #1F2937;
|
|
||||||
padding-top: 56px; /* Offset for fixed navbar */
|
|
||||||
}
|
|
||||||
|
|
||||||
h1, h2, h3, h4, h5, h6 {
|
|
||||||
font-family: 'Georgia', serif;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Navbar */
|
|
||||||
.navbar {
|
|
||||||
box-shadow: 0 2px 4px rgba(0,0,0,0.05);
|
|
||||||
}
|
|
||||||
|
|
||||||
.navbar-brand {
|
|
||||||
font-family: 'Georgia', serif;
|
|
||||||
font-weight: 700;
|
|
||||||
font-size: 1.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Hero Section */
|
|
||||||
.hero {
|
|
||||||
position: relative;
|
|
||||||
padding: 8rem 0;
|
|
||||||
background-image: linear-gradient(rgba(0, 0, 0, 0.5), rgba(0, 0, 0, 0.5)), url('https://picsum.photos/seed/medhero/1600/900');
|
|
||||||
background-size: cover;
|
|
||||||
background-position: center;
|
|
||||||
background-attachment: fixed;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hero h1 {
|
|
||||||
font-weight: 700;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Buttons */
|
|
||||||
.btn-primary {
|
|
||||||
background-color: #3B82F6;
|
|
||||||
border-color: #3B82F6;
|
|
||||||
padding: 0.75rem 1.5rem;
|
|
||||||
border-radius: 0.5rem;
|
|
||||||
transition: all 0.3s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-primary:hover {
|
|
||||||
background-color: #2563EB;
|
|
||||||
border-color: #2563EB;
|
|
||||||
transform: translateY(-2px);
|
|
||||||
box-shadow: 0 4px 12px rgba(0,0,0,0.1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Cards */
|
|
||||||
.card {
|
|
||||||
border: none;
|
|
||||||
border-radius: 1rem;
|
|
||||||
transition: transform 0.3s ease, box-shadow 0.3s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card:hover {
|
|
||||||
transform: translateY(-5px);
|
|
||||||
box-shadow: 0 8px 20px rgba(0,0,0,0.08);
|
|
||||||
}
|
|
||||||
|
|
||||||
#checker .card {
|
|
||||||
border-radius: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Forms */
|
|
||||||
.form-control {
|
|
||||||
border-radius: 0.5rem;
|
|
||||||
padding: 0.75rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-control:focus {
|
|
||||||
box-shadow: 0 0 0 0.25rem rgba(59, 130, 246, 0.25);
|
|
||||||
border-color: #3B82F6;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Interaction Result */
|
|
||||||
#interaction-result .alert {
|
|
||||||
border-radius: 0.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.alert-success {
|
|
||||||
background-color: #E0F2F1; /* Lighter green */
|
|
||||||
color: #0D6B61;
|
|
||||||
border-color: #B2DFDB;
|
|
||||||
}
|
|
||||||
|
|
||||||
.alert-warning {
|
|
||||||
background-color: #FFF3E0; /* Lighter orange */
|
|
||||||
color: #A65B00;
|
|
||||||
border-color: #FFE0B2;
|
|
||||||
}
|
|
||||||
|
|
||||||
.alert-danger {
|
|
||||||
background-color: #FFEBEE; /* Lighter red */
|
|
||||||
color: #B71C1C;
|
|
||||||
border-color: #FFCDD2;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Footer */
|
|
||||||
footer a:hover {
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Expiry Tracker */
|
|
||||||
#medicine-list-container .list-group-item {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.expiry-date {
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
|
||||||
|
|
||||||
.expiry-soon {
|
|
||||||
color: #A65B00; /* Orange from alert-warning */
|
|
||||||
}
|
|
||||||
|
|
||||||
.expired {
|
|
||||||
color: #B71C1C; /* Red from alert-danger */
|
|
||||||
}
|
|
||||||
|
|
||||||
#medicine-list-container .btn-sm {
|
|
||||||
--bs-btn-padding-y: .2rem;
|
|
||||||
--bs-btn-padding-x: .4rem;
|
|
||||||
--bs-btn-font-size: .75rem;
|
|
||||||
}
|
|
||||||
@ -1,250 +0,0 @@
|
|||||||
document.addEventListener('DOMContentLoaded', function () {
|
|
||||||
|
|
||||||
// Smooth scrolling for anchor links
|
|
||||||
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
|
|
||||||
anchor.addEventListener('click', function (e) {
|
|
||||||
e.preventDefault();
|
|
||||||
document.querySelector(this.getAttribute('href')).scrollIntoView({
|
|
||||||
behavior: 'smooth'
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// Interaction Checker Logic
|
|
||||||
const interactionForm = document.getElementById('interaction-form');
|
|
||||||
const resultDiv = document.getElementById('interaction-result');
|
|
||||||
|
|
||||||
if (interactionForm) {
|
|
||||||
interactionForm.addEventListener('submit', function (e) {
|
|
||||||
e.preventDefault();
|
|
||||||
const drugA = document.getElementById('drugA').value.trim();
|
|
||||||
const drugB = document.getElementById('drugB').value.trim();
|
|
||||||
|
|
||||||
resultDiv.className = 'mt-4 d-none'; // Hide previous result
|
|
||||||
resultDiv.innerHTML = '<div class="d-flex justify-content-center"><div class="spinner-border text-primary" role="status"><span class="visually-hidden">Loading...</span></div></div>';
|
|
||||||
resultDiv.classList.remove('d-none');
|
|
||||||
|
|
||||||
setTimeout(() => {
|
|
||||||
let resultMessage = '';
|
|
||||||
let alertClass = 'alert-success';
|
|
||||||
|
|
||||||
if (drugA.toLowerCase() === 'warfarin' && drugB.toLowerCase() === 'aspirin' || drugA.toLowerCase() === 'aspirin' && drugB.toLowerCase() === 'warfarin') {
|
|
||||||
resultMessage = `<strong>High Risk Interaction:</strong> Combining ${drugA} and ${drugB} increases the risk of bleeding. Consult your doctor immediately.`;
|
|
||||||
alertClass = 'alert-danger';
|
|
||||||
} else if (drugA === '' || drugB === '') {
|
|
||||||
resultMessage = 'Please enter both drug names.';
|
|
||||||
alertClass = 'alert-warning';
|
|
||||||
} else {
|
|
||||||
resultMessage = `<strong>No Major Interaction Found:</strong> No significant interaction was found between ${drugA} and ${drugB}. This is not a substitute for professional medical advice.`;
|
|
||||||
}
|
|
||||||
|
|
||||||
resultDiv.innerHTML = `<div class="alert ${alertClass}">${resultMessage}</div>`;
|
|
||||||
}, 1500);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Contact Form Logic
|
|
||||||
const contactForm = document.getElementById('contact-form');
|
|
||||||
const contactToastEl = document.getElementById('contact-toast');
|
|
||||||
const contactToast = new bootstrap.Toast(contactToastEl);
|
|
||||||
|
|
||||||
if (contactForm) {
|
|
||||||
contactForm.addEventListener('submit', function(e) {
|
|
||||||
e.preventDefault();
|
|
||||||
const name = document.getElementById('name').value;
|
|
||||||
const email = document.getElementById('email').value;
|
|
||||||
const message = document.getElementById('message').value;
|
|
||||||
const submitButton = contactForm.querySelector('button[type="submit"]');
|
|
||||||
|
|
||||||
const formData = new FormData();
|
|
||||||
formData.append('name', name);
|
|
||||||
formData.append('email', email);
|
|
||||||
formData.append('message', message);
|
|
||||||
|
|
||||||
submitButton.disabled = true;
|
|
||||||
submitButton.innerHTML = '<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span> Sending...';
|
|
||||||
|
|
||||||
fetch('contact.php', {
|
|
||||||
method: 'POST',
|
|
||||||
body: formData
|
|
||||||
})
|
|
||||||
.then(response => response.json())
|
|
||||||
.then(data => {
|
|
||||||
const toastBody = contactToastEl.querySelector('.toast-body');
|
|
||||||
if (data.success) {
|
|
||||||
toastBody.textContent = data.message;
|
|
||||||
contactToastEl.classList.remove('bg-danger');
|
|
||||||
contactToastEl.classList.add('bg-success', 'text-white');
|
|
||||||
contactForm.reset();
|
|
||||||
} else {
|
|
||||||
toastBody.textContent = data.message || 'An error occurred.';
|
|
||||||
contactToastEl.classList.remove('bg-success');
|
|
||||||
contactToastEl.classList.add('bg-danger', 'text-white');
|
|
||||||
}
|
|
||||||
contactToast.show();
|
|
||||||
})
|
|
||||||
.catch(error => {
|
|
||||||
const toastBody = contactToastEl.querySelector('.toast-body');
|
|
||||||
toastBody.textContent = 'A network error occurred. Please try again.';
|
|
||||||
contactToastEl.classList.remove('bg-success');
|
|
||||||
contactToastEl.classList.add('bg-danger', 'text-white');
|
|
||||||
contactToast.show();
|
|
||||||
})
|
|
||||||
.finally(() => {
|
|
||||||
submitButton.disabled = false;
|
|
||||||
submitButton.innerHTML = 'Send Message';
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Expiry Alert Tracker Logic
|
|
||||||
const medicineForm = document.getElementById('medicine-form');
|
|
||||||
const medicineListContainer = document.getElementById('medicine-list-container');
|
|
||||||
|
|
||||||
// Function to calculate date difference and apply styling
|
|
||||||
const getExpiryStatus = (expiryDate) => {
|
|
||||||
const now = new Date();
|
|
||||||
const expiry = new Date(expiryDate);
|
|
||||||
// Reset time part to compare dates only
|
|
||||||
now.setHours(0, 0, 0, 0);
|
|
||||||
expiry.setHours(0, 0, 0, 0);
|
|
||||||
|
|
||||||
const diffTime = expiry - now;
|
|
||||||
const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24));
|
|
||||||
|
|
||||||
if (diffDays < 0) {
|
|
||||||
return { text: `Expired on ${expiry.toLocaleDateString()}`, className: 'expired' };
|
|
||||||
}
|
|
||||||
if (diffDays <= 7) {
|
|
||||||
return { text: `Expires in ${diffDays} day(s)`, className: 'expiry-soon' };
|
|
||||||
}
|
|
||||||
return { text: `Expires on ${expiry.toLocaleDateString()}`, className: '' };
|
|
||||||
};
|
|
||||||
|
|
||||||
// Function to render medicines
|
|
||||||
const renderMedicines = (medicines) => {
|
|
||||||
if (!medicines || medicines.length === 0) {
|
|
||||||
medicineListContainer.innerHTML = '<p class="text-muted">No medicines being tracked yet.</p>';
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const list = document.createElement('ul');
|
|
||||||
list.className = 'list-group list-group-flush';
|
|
||||||
|
|
||||||
medicines.forEach(medicine => {
|
|
||||||
const status = getExpiryStatus(medicine.expiry_date);
|
|
||||||
const item = document.createElement('li');
|
|
||||||
item.className = 'list-group-item';
|
|
||||||
item.innerHTML = `
|
|
||||||
<div>
|
|
||||||
<strong>${medicine.medicine_name}</strong>
|
|
||||||
<br>
|
|
||||||
<small class="expiry-date ${status.className}">${status.text}</small>
|
|
||||||
</div>
|
|
||||||
<button class="btn btn-sm btn-outline-danger delete-medicine" data-id="${medicine.id}">Remove</button>
|
|
||||||
`;
|
|
||||||
list.appendChild(item);
|
|
||||||
});
|
|
||||||
|
|
||||||
medicineListContainer.innerHTML = '';
|
|
||||||
medicineListContainer.appendChild(list);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Function to fetch medicines from the server
|
|
||||||
const fetchMedicines = () => {
|
|
||||||
fetch('medicines.php?action=get')
|
|
||||||
.then(response => response.json())
|
|
||||||
.then(data => {
|
|
||||||
if (data.success) {
|
|
||||||
renderMedicines(data.medicines);
|
|
||||||
} else {
|
|
||||||
console.error('Failed to fetch medicines:', data.message);
|
|
||||||
medicineListContainer.innerHTML = '<p class="text-danger">Could not load medicines.</p>';
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch(error => {
|
|
||||||
console.error('Error fetching medicines:', error);
|
|
||||||
medicineListContainer.innerHTML = '<p class="text-danger">Error loading medicines.</p>';
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
// Event listener for adding a new medicine
|
|
||||||
if (medicineForm) {
|
|
||||||
medicineForm.addEventListener('submit', function (e) {
|
|
||||||
e.preventDefault();
|
|
||||||
const medicineName = document.getElementById('medicine_name').value.trim();
|
|
||||||
const expiryDate = document.getElementById('expiry_date').value;
|
|
||||||
const submitButton = medicineForm.querySelector('button[type="submit"]');
|
|
||||||
|
|
||||||
const formData = new FormData();
|
|
||||||
formData.append('action', 'add');
|
|
||||||
formData.append('medicine_name', medicineName);
|
|
||||||
formData.append('expiry_date', expiryDate);
|
|
||||||
|
|
||||||
submitButton.disabled = true;
|
|
||||||
submitButton.innerHTML = '<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span> Adding...';
|
|
||||||
|
|
||||||
fetch('medicines.php', {
|
|
||||||
method: 'POST',
|
|
||||||
body: formData
|
|
||||||
})
|
|
||||||
.then(response => response.json())
|
|
||||||
.then(data => {
|
|
||||||
if (data.success) {
|
|
||||||
medicineForm.reset();
|
|
||||||
fetchMedicines(); // Refresh the list
|
|
||||||
} else {
|
|
||||||
alert(`Error: ${data.message}`);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch(error => {
|
|
||||||
alert('A network error occurred.');
|
|
||||||
})
|
|
||||||
.finally(() => {
|
|
||||||
submitButton.disabled = false;
|
|
||||||
submitButton.innerHTML = 'Add to Tracker';
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Event listener for deleting a medicine (using event delegation)
|
|
||||||
if (medicineListContainer) {
|
|
||||||
medicineListContainer.addEventListener('click', function(e) {
|
|
||||||
if (e.target && e.target.classList.contains('delete-medicine')) {
|
|
||||||
const medicineId = e.target.getAttribute('data-id');
|
|
||||||
if (!confirm('Are you sure you want to remove this medicine?')) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const formData = new FormData();
|
|
||||||
formData.append('action', 'delete');
|
|
||||||
formData.append('id', medicineId);
|
|
||||||
|
|
||||||
e.target.disabled = true;
|
|
||||||
|
|
||||||
fetch('medicines.php', {
|
|
||||||
method: 'POST',
|
|
||||||
body: formData
|
|
||||||
})
|
|
||||||
.then(response => response.json())
|
|
||||||
.then(data => {
|
|
||||||
if (data.success) {
|
|
||||||
fetchMedicines(); // Refresh the list
|
|
||||||
} else {
|
|
||||||
alert(`Error: ${data.message}`);
|
|
||||||
e.target.disabled = false;
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch(error => {
|
|
||||||
alert('A network error occurred.');
|
|
||||||
e.target.disabled = false;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initial fetch of medicines when the page loads
|
|
||||||
if (medicineListContainer) {
|
|
||||||
fetchMedicines();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
60
contact.php
60
contact.php
@ -1,60 +0,0 @@
|
|||||||
<?php
|
|
||||||
header('Content-Type: application/json');
|
|
||||||
|
|
||||||
require_once __DIR__ . '/db/config.php';
|
|
||||||
require_once __DIR__ . '/mail/MailService.php';
|
|
||||||
|
|
||||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
|
||||||
echo json_encode(['success' => false, 'message' => 'Invalid request method.']);
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
$name = trim($_POST['name'] ?? '');
|
|
||||||
$email = trim($_POST['email'] ?? '');
|
|
||||||
$message = trim($_POST['message'] ?? '');
|
|
||||||
|
|
||||||
if (empty($name) || empty($email) || empty($message)) {
|
|
||||||
echo json_encode(['success' => false, 'message' => 'Please fill out all fields.']);
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
|
|
||||||
echo json_encode(['success' => false, 'message' => 'Please provide a valid email address.']);
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
$pdo = db();
|
|
||||||
|
|
||||||
// Create table if it doesn't exist (idempotent)
|
|
||||||
$pdo->exec("CREATE TABLE IF NOT EXISTS contact_submissions (
|
|
||||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
|
||||||
name VARCHAR(255) NOT NULL,
|
|
||||||
email VARCHAR(255) NOT NULL,
|
|
||||||
message TEXT NOT NULL,
|
|
||||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
||||||
)");
|
|
||||||
|
|
||||||
// Insert submission
|
|
||||||
$stmt = $pdo->prepare("INSERT INTO contact_submissions (name, email, message) VALUES (?, ?, ?)");
|
|
||||||
$stmt->execute([$name, $email, $message]);
|
|
||||||
|
|
||||||
// Send email notification
|
|
||||||
// The recipient is determined by the MAIL_TO env var by default.
|
|
||||||
$mailResult = MailService::sendContactMessage($name, $email, $message);
|
|
||||||
|
|
||||||
if ($mailResult['success']) {
|
|
||||||
echo json_encode(['success' => true, 'message' => 'Thank you! Your message has been sent.']);
|
|
||||||
} else {
|
|
||||||
// Still a success for the user, but log the mail error.
|
|
||||||
error_log("Contact form saved to DB, but mail sending failed: " . ($mailResult['error'] ?? 'Unknown error'));
|
|
||||||
echo json_encode(['success' => true, 'message' => 'Thank you! Your message has been received.']);
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (PDOException $e) {
|
|
||||||
error_log("Database error: " . $e->getMessage());
|
|
||||||
echo json_encode(['success' => false, 'message' => 'A server error occurred while saving your message.']);
|
|
||||||
} catch (Exception $e) {
|
|
||||||
error_log("General error: " . $e->getMessage());
|
|
||||||
echo json_encode(['success' => false, 'message' => 'A server error occurred.']);
|
|
||||||
}
|
|
||||||
@ -1,33 +0,0 @@
|
|||||||
<?php
|
|
||||||
require_once __DIR__ . '/config.php';
|
|
||||||
|
|
||||||
try {
|
|
||||||
$pdo = db();
|
|
||||||
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
|
||||||
|
|
||||||
$migrationsDir = __DIR__ . '/migrations';
|
|
||||||
if (!is_dir($migrationsDir)) {
|
|
||||||
mkdir($migrationsDir, 0775, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
$files = glob($migrationsDir . '/*.sql');
|
|
||||||
sort($files);
|
|
||||||
|
|
||||||
foreach ($files as $file) {
|
|
||||||
$sql = file_get_contents($file);
|
|
||||||
if (!empty(trim($sql))) {
|
|
||||||
$pdo->exec($sql);
|
|
||||||
echo "Executed migration: " . basename($file) . "
|
|
||||||
";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
echo "Migrations completed successfully.
|
|
||||||
";
|
|
||||||
|
|
||||||
} catch (PDOException $e) {
|
|
||||||
http_response_code(500);
|
|
||||||
die("Migration failed: " . $e->getMessage() . "
|
|
||||||
");
|
|
||||||
}
|
|
||||||
?>
|
|
||||||
@ -1,6 +0,0 @@
|
|||||||
CREATE TABLE IF NOT EXISTS `medicines` (
|
|
||||||
`id` INT AUTO_INCREMENT PRIMARY KEY,
|
|
||||||
`medicine_name` VARCHAR(255) NOT NULL,
|
|
||||||
`expiry_date` DATE NOT NULL,
|
|
||||||
`added_on` TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
||||||
);
|
|
||||||
339
index.php
339
index.php
@ -1,217 +1,150 @@
|
|||||||
<!DOCTYPE html>
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
@ini_set('display_errors', '1');
|
||||||
|
@error_reporting(E_ALL);
|
||||||
|
@date_default_timezone_set('UTC');
|
||||||
|
|
||||||
|
$phpVersion = PHP_VERSION;
|
||||||
|
$now = date('Y-m-d H:i:s');
|
||||||
|
?>
|
||||||
|
<!doctype html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="utf-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
<title>MediScan - Smart Medicine Management</title>
|
<title>New Style</title>
|
||||||
<meta name="description" content="MediScan: Effortlessly check drug interactions and expiry dates with real-time alerts for patients, pharmacists, and doctors.">
|
<?php
|
||||||
<meta name="keywords" content="medication management, drug interaction checker, pill reminder, pharmacy app, healthcare technology, medicine scanner, expiry date tracker, patient safety, digital health, medical app, prescription management, drug alerts">
|
// Read project preview data from environment
|
||||||
|
$projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? '';
|
||||||
<!-- Open Graph / Facebook -->
|
$projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
|
||||||
<meta property="og:type" content="website">
|
?>
|
||||||
<meta property="og:title" content="MediScan - Smart Medicine Management">
|
<?php if ($projectDescription): ?>
|
||||||
<meta property="og:description" content="MediScan: Effortlessly check drug interactions and expiry dates with real-time alerts for patients, pharmacists, and doctors.">
|
<!-- Meta description -->
|
||||||
<meta property="og:image" content="https://project-screens.s3.amazonaws.com/screenshots/34616/app-hero-20251003-083323.png">
|
<meta name="description" content='<?= htmlspecialchars($projectDescription) ?>' />
|
||||||
|
<!-- Open Graph meta tags -->
|
||||||
<!-- Twitter -->
|
<meta property="og:description" content="<?= htmlspecialchars($projectDescription) ?>" />
|
||||||
<meta name="twitter:card" content="summary_large_image">
|
<!-- Twitter meta tags -->
|
||||||
<meta name="twitter:title" content="MediScan - Smart Medicine Management">
|
<meta property="twitter:description" content="<?= htmlspecialchars($projectDescription) ?>" />
|
||||||
<meta name="twitter:description" content="MediScan: Effortlessly check drug interactions and expiry dates with real-time alerts for patients, pharmacists, and doctors.">
|
<?php endif; ?>
|
||||||
<meta name="twitter:image" content="https://project-screens.s3.amazonaws.com/screenshots/34616/app-hero-20251003-083323.png">
|
<?php if ($projectImageUrl): ?>
|
||||||
|
<!-- Open Graph image -->
|
||||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
|
<meta property="og:image" content="<?= htmlspecialchars($projectImageUrl) ?>" />
|
||||||
|
<!-- Twitter image -->
|
||||||
|
<meta property="twitter:image" content="<?= htmlspecialchars($projectImageUrl) ?>" />
|
||||||
|
<?php endif; ?>
|
||||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;700&family=Georgia:wght@700&display=swap" rel="stylesheet">
|
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;700&display=swap" rel="stylesheet">
|
||||||
<link rel="stylesheet" href="assets/css/custom.css?v=<?php echo time(); ?>">
|
<style>
|
||||||
|
:root {
|
||||||
|
--bg-color-start: #6a11cb;
|
||||||
|
--bg-color-end: #2575fc;
|
||||||
|
--text-color: #ffffff;
|
||||||
|
--card-bg-color: rgba(255, 255, 255, 0.01);
|
||||||
|
--card-border-color: rgba(255, 255, 255, 0.1);
|
||||||
|
}
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
font-family: 'Inter', sans-serif;
|
||||||
|
background: linear-gradient(45deg, var(--bg-color-start), var(--bg-color-end));
|
||||||
|
color: var(--text-color);
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
min-height: 100vh;
|
||||||
|
text-align: center;
|
||||||
|
overflow: hidden;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
body::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background-image: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100" viewBox="0 0 100 100"><path d="M-10 10L110 10M10 -10L10 110" stroke-width="1" stroke="rgba(255,255,255,0.05)"/></svg>');
|
||||||
|
animation: bg-pan 20s linear infinite;
|
||||||
|
z-index: -1;
|
||||||
|
}
|
||||||
|
@keyframes bg-pan {
|
||||||
|
0% { background-position: 0% 0%; }
|
||||||
|
100% { background-position: 100% 100%; }
|
||||||
|
}
|
||||||
|
main {
|
||||||
|
padding: 2rem;
|
||||||
|
}
|
||||||
|
.card {
|
||||||
|
background: var(--card-bg-color);
|
||||||
|
border: 1px solid var(--card-border-color);
|
||||||
|
border-radius: 16px;
|
||||||
|
padding: 2rem;
|
||||||
|
backdrop-filter: blur(20px);
|
||||||
|
-webkit-backdrop-filter: blur(20px);
|
||||||
|
box-shadow: 0 8px 32px 0 rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
.loader {
|
||||||
|
margin: 1.25rem auto 1.25rem;
|
||||||
|
width: 48px;
|
||||||
|
height: 48px;
|
||||||
|
border: 3px solid rgba(255, 255, 255, 0.25);
|
||||||
|
border-top-color: #fff;
|
||||||
|
border-radius: 50%;
|
||||||
|
animation: spin 1s linear infinite;
|
||||||
|
}
|
||||||
|
@keyframes spin {
|
||||||
|
from { transform: rotate(0deg); }
|
||||||
|
to { transform: rotate(360deg); }
|
||||||
|
}
|
||||||
|
.hint {
|
||||||
|
opacity: 0.9;
|
||||||
|
}
|
||||||
|
.sr-only {
|
||||||
|
position: absolute;
|
||||||
|
width: 1px; height: 1px;
|
||||||
|
padding: 0; margin: -1px;
|
||||||
|
overflow: hidden;
|
||||||
|
clip: rect(0, 0, 0, 0);
|
||||||
|
white-space: nowrap; border: 0;
|
||||||
|
}
|
||||||
|
h1 {
|
||||||
|
font-size: 3rem;
|
||||||
|
font-weight: 700;
|
||||||
|
margin: 0 0 1rem;
|
||||||
|
letter-spacing: -1px;
|
||||||
|
}
|
||||||
|
p {
|
||||||
|
margin: 0.5rem 0;
|
||||||
|
font-size: 1.1rem;
|
||||||
|
}
|
||||||
|
code {
|
||||||
|
background: rgba(0,0,0,0.2);
|
||||||
|
padding: 2px 6px;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
||||||
|
}
|
||||||
|
footer {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 1rem;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
opacity: 0.7;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|
||||||
<nav class="navbar navbar-expand-lg navbar-light bg-light fixed-top">
|
|
||||||
<div class="container">
|
|
||||||
<a class="navbar-brand" href="#">MediScan</a>
|
|
||||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav">
|
|
||||||
<span class="navbar-toggler-icon"></span>
|
|
||||||
</button>
|
|
||||||
<div class="collapse navbar-collapse" id="navbarNav">
|
|
||||||
<ul class="navbar-nav ms-auto">
|
|
||||||
<li class="nav-item"><a class="nav-link" href="#features">Features</a></li>
|
|
||||||
<li class="nav-item"><a class="nav-link" href="#about">About</a></li>
|
|
||||||
<li class="nav-item"><a class="nav-link" href="#expiry-tracker">Expiry Tracker</a></li>
|
|
||||||
<li class="nav-item"><a class="nav-link" href="#contact">Contact</a></li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</nav>
|
|
||||||
|
|
||||||
<header id="hero" class="hero">
|
|
||||||
<div class="container text-center text-white">
|
|
||||||
<h1 class="display-3">Effortless Medication Safety.</h1>
|
|
||||||
<p class="lead my-4">Scan, check, and manage your medications with confidence. Real-time interaction alerts and expiry tracking at your fingertips.</p>
|
|
||||||
<a href="#checker" class="btn btn-primary btn-lg">Check Interactions Now</a>
|
|
||||||
</div>
|
|
||||||
</header>
|
|
||||||
|
|
||||||
<main>
|
<main>
|
||||||
<section id="checker" class="py-5">
|
|
||||||
<div class="container">
|
|
||||||
<div class="row justify-content-center">
|
|
||||||
<div class="col-lg-8">
|
|
||||||
<div class="card shadow-sm">
|
|
||||||
<div class="card-body p-5">
|
|
||||||
<h2 class="text-center mb-4">Drug Interaction Checker</h2>
|
|
||||||
<form id="interaction-form">
|
|
||||||
<div class="row g-3">
|
|
||||||
<div class="col-md-6">
|
|
||||||
<label for="drugA" class="form-label">Drug A</label>
|
|
||||||
<input type="text" class="form-control" id="drugA" placeholder="e.g., Warfarin" required>
|
|
||||||
</div>
|
|
||||||
<div class="col-md-6">
|
|
||||||
<label for="drugB" class="form-label">Drug B</label>
|
|
||||||
<input type="text" class="form-control" id="drugB" placeholder="e.g., Aspirin" required>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="text-center mt-4">
|
|
||||||
<button type="submit" class="btn btn-primary">Check for Interactions</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
<div id="interaction-result" class="mt-4 d-none"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section id="features" class="py-5 bg-light">
|
|
||||||
<div class="container">
|
|
||||||
<h2 class="text-center mb-5">Core Features</h2>
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-4 mb-4">
|
|
||||||
<div class="card h-100 text-center">
|
|
||||||
<div class="card-body">
|
|
||||||
<h5 class="card-title">Instant Scanning</h5>
|
|
||||||
<p class="card-text">Use your camera to scan medicine barcodes or packaging to instantly pull up details.</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col-md-4 mb-4">
|
|
||||||
<div class="card h-100 text-center">
|
|
||||||
<div class="card-body">
|
|
||||||
<h5 class="card-title">Interaction Alerts</h5>
|
|
||||||
<p class="card-text">Cross-reference multiple medications to receive clear warnings about potential drug interactions.</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col-md-4 mb-4">
|
|
||||||
<div class="card h-100 text-center">
|
|
||||||
<div class="card-body">
|
|
||||||
<h5 class="card-title">Expiry Tracking</h5>
|
|
||||||
<p class="card-text">Never wonder about an expiry date again. Get automatic reminders for your medicines.</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section id="about" class="py-5">
|
|
||||||
<div class="container">
|
|
||||||
<div class="row align-items-center">
|
|
||||||
<div class="col-md-6">
|
|
||||||
<h2>About MediScan</h2>
|
|
||||||
<p>MediScan was born from a need for a simpler, more accessible way to manage medication safety. Our mission is to empower patients, pharmacists, and doctors with a tool that provides clear, instant, and reliable information. By bridging the gap between medical data and the user, we aim to reduce adverse drug reactions and improve overall health outcomes.</p>
|
|
||||||
</div>
|
|
||||||
<div class="col-md-6">
|
|
||||||
<img src="https://picsum.photos/seed/medabout/800/600" class="img-fluid rounded shadow" alt="A diverse group of healthcare professionals collaborating.">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section id="expiry-tracker" class="py-5">
|
|
||||||
<div class="container">
|
|
||||||
<h2 class="text-center mb-5">Expiry Alert Tracker</h2>
|
|
||||||
<div class="row">
|
|
||||||
<!-- Add Medicine Form -->
|
|
||||||
<div class="col-lg-4 mb-4">
|
|
||||||
<div class="card h-100">
|
|
||||||
<div class="card-body p-4">
|
|
||||||
<h5 class="card-title mb-3">Add New Medicine</h5>
|
|
||||||
<form id="medicine-form">
|
|
||||||
<div class="mb-3">
|
|
||||||
<label for="medicine_name" class="form-label">Medicine Name</label>
|
|
||||||
<input type="text" class="form-control" id="medicine_name" placeholder="e.g., Atorvastatin" required>
|
|
||||||
</div>
|
|
||||||
<div class="mb-3">
|
|
||||||
<label for="expiry_date" class="form-label">Expiry Date</label>
|
|
||||||
<input type="date" class="form-control" id="expiry_date" required>
|
|
||||||
</div>
|
|
||||||
<button type="submit" class="btn btn-primary w-100">Add to Tracker</button>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<!-- Medicine List -->
|
|
||||||
<div class="col-lg-8">
|
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="card-body p-4">
|
<h1>Analyzing your requirements and generating your website…</h1>
|
||||||
<h5 class="card-title mb-3">Tracked Medicines</h5>
|
<div class="loader" role="status" aria-live="polite" aria-label="Applying initial changes">
|
||||||
<div id="medicine-list-container">
|
<span class="sr-only">Loading…</span>
|
||||||
<div class="d-flex justify-content-center"><div class="spinner-border text-primary" role="status"><span class="visually-hidden">Loading...</span></div></div>
|
|
||||||
</div>
|
</div>
|
||||||
|
<p class="hint"><?= ($_SERVER['HTTP_HOST'] ?? '') === 'appwizzy.com' ? 'AppWizzy' : 'Flatlogic' ?> AI is collecting your requirements and applying the first changes.</p>
|
||||||
|
<p class="hint">This page will update automatically as the plan is implemented.</p>
|
||||||
|
<p>Runtime: PHP <code><?= htmlspecialchars($phpVersion) ?></code> — UTC <code><?= htmlspecialchars($now) ?></code></p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section id="contact" class="py-5 bg-light">
|
|
||||||
<div class="container">
|
|
||||||
<h2 class="text-center mb-4">Get In Touch</h2>
|
|
||||||
<div class="row justify-content-center">
|
|
||||||
<div class="col-lg-6">
|
|
||||||
<form id="contact-form">
|
|
||||||
<div id="contact-alert" class="d-none"></div>
|
|
||||||
<div class="mb-3">
|
|
||||||
<label for="name" class="form-label">Name</label>
|
|
||||||
<input type="text" class="form-control" id="name" required>
|
|
||||||
</div>
|
|
||||||
<div class="mb-3">
|
|
||||||
<label for="email" class="form-label">Email</label>
|
|
||||||
<input type="email" class="form-control" id="email" required>
|
|
||||||
</div>
|
|
||||||
<div class="mb-3">
|
|
||||||
<label for="message" class="form-label">Message</label>
|
|
||||||
<textarea class="form-control" id="message" rows="4" required></textarea>
|
|
||||||
</div>
|
|
||||||
<button type="submit" class="btn btn-primary w-100">Send Message</button>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
</main>
|
</main>
|
||||||
|
<footer>
|
||||||
<footer class="py-4 bg-dark text-white text-center">
|
Page updated: <?= htmlspecialchars($now) ?> (UTC)
|
||||||
<div class="container">
|
|
||||||
<p>© <?php echo date("Y"); ?> MediScan. All Rights Reserved. | <a href="/privacy.php" class="text-white">Privacy Policy</a></p>
|
|
||||||
</div>
|
|
||||||
</footer>
|
</footer>
|
||||||
|
|
||||||
<div class="toast-container position-fixed bottom-0 end-0 p-3">
|
|
||||||
<div id="contact-toast" class="toast" role="alert" aria-live="assertive" aria-atomic="true">
|
|
||||||
<div class="toast-header">
|
|
||||||
<strong class="me-auto">MediScan</strong>
|
|
||||||
<button type="button" class="btn-close" data-bs-dismiss="toast" aria-label="Close"></button>
|
|
||||||
</div>
|
|
||||||
<div class="toast-body">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
|
|
||||||
<script src="assets/js/main.js?v=<?php echo time(); ?>"></script>
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
@ -1,45 +0,0 @@
|
|||||||
<?php
|
|
||||||
require_once __DIR__ . '/db/config.php';
|
|
||||||
|
|
||||||
header('Content-Type: application/json');
|
|
||||||
|
|
||||||
$response = ['success' => false, 'message' => 'Invalid request.'];
|
|
||||||
$action = $_REQUEST['action'] ?? null;
|
|
||||||
|
|
||||||
try {
|
|
||||||
$pdo = db();
|
|
||||||
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
|
||||||
|
|
||||||
if ($_SERVER['REQUEST_METHOD'] === 'POST' && $action === 'add') {
|
|
||||||
$medicineName = trim($_POST['medicine_name'] ?? '');
|
|
||||||
$expiryDate = trim($_POST['expiry_date'] ?? '');
|
|
||||||
|
|
||||||
if (empty($medicineName) || empty($expiryDate)) {
|
|
||||||
$response['message'] = 'Medicine name and expiry date are required.';
|
|
||||||
} else {
|
|
||||||
$stmt = $pdo->prepare("INSERT INTO medicines (medicine_name, expiry_date) VALUES (:name, :date)");
|
|
||||||
$stmt->execute(['name' => $medicineName, 'date' => $expiryDate]);
|
|
||||||
$response = ['success' => true, 'message' => 'Medicine added successfully.'];
|
|
||||||
}
|
|
||||||
} elseif ($_SERVER['REQUEST_METHOD'] === 'GET' && $action === 'get') {
|
|
||||||
$stmt = $pdo->query("SELECT id, medicine_name, expiry_date FROM medicines ORDER BY expiry_date ASC");
|
|
||||||
$medicines = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
|
||||||
$response = ['success' => true, 'medicines' => $medicines];
|
|
||||||
} elseif ($_SERVER['REQUEST_METHOD'] === 'POST' && $action === 'delete') {
|
|
||||||
$id = $_POST['id'] ?? null;
|
|
||||||
if ($id) {
|
|
||||||
$stmt = $pdo->prepare("DELETE FROM medicines WHERE id = :id");
|
|
||||||
$stmt->execute(['id' => $id]);
|
|
||||||
$response = ['success' => true, 'message' => 'Medicine removed.'];
|
|
||||||
} else {
|
|
||||||
$response['message'] = 'Medicine ID is required.';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (PDOException $e) {
|
|
||||||
http_response_code(500);
|
|
||||||
$response['message'] = 'Database error: ' . $e->getMessage();
|
|
||||||
}
|
|
||||||
|
|
||||||
echo json_encode($response);
|
|
||||||
?>
|
|
||||||
28
privacy.php
28
privacy.php
@ -1,28 +0,0 @@
|
|||||||
<?php
|
|
||||||
// Basic privacy policy page stub
|
|
||||||
?>
|
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
||||||
<title>Privacy Policy - MediScan</title>
|
|
||||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
|
|
||||||
<link rel="stylesheet" href="assets/css/custom.css?v=<?php echo time(); ?>">
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div class="container py-5">
|
|
||||||
<h1>Privacy Policy</h1>
|
|
||||||
<p>This is a placeholder for the Privacy Policy.</p>
|
|
||||||
<p>Your privacy is important to us. It is MediScan's policy to respect your privacy regarding any information we may collect from you across our website.</p>
|
|
||||||
<p>We only ask for personal information when we truly need it to provide a service to you. We collect it by fair and lawful means, with your knowledge and consent. We also let you know why we’re collecting it and how it will be used.</p>
|
|
||||||
<p>We only retain collected information for as long as necessary to provide you with your requested service. What data we store, we’ll protect within commercially acceptable means to prevent loss and theft, as well as unauthorized access, disclosure, copying, use or modification.</p>
|
|
||||||
<p>We don’t share any personally identifying information publicly or with third-parties, except when required to by law.</p>
|
|
||||||
<p>Our website may link to external sites that are not operated by us. Please be aware that we have no control over the content and practices of these sites, and cannot accept responsibility or liability for their respective privacy policies.</p>
|
|
||||||
<p>You are free to refuse our request for your personal information, with the understanding that we may be unable to provide you with some of your desired services.</p>
|
|
||||||
<p>Your continued use of our website will be regarded as acceptance of our practices around privacy and personal information. If you have any questions about how we handle user data and personal information, feel free to contact us.</p>
|
|
||||||
<p>This policy is effective as of <?php echo date("F j, Y"); ?>.</p>
|
|
||||||
<a href="/" class="btn btn-primary">Back to Home</a>
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
Loading…
x
Reference in New Issue
Block a user