Auto commit: 2025-09-25T10:30:50.227Z

This commit is contained in:
Flatlogic Bot 2025-09-25 10:30:50 +00:00
parent c8f09891b3
commit d7a8bedac1
3 changed files with 461 additions and 122 deletions

120
assets/css/custom.css Normal file
View File

@ -0,0 +1,120 @@
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap');
:root {
--primary-color: #4A90E2;
--secondary-color: #50E3C2;
--background-color: #F4F7F9;
--surface-color: #FFFFFF;
--text-color: #333333;
--border-color: #E0E6ED;
--border-radius: 0.5rem;
--shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
--shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
}
body {
font-family: 'Inter', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
background-color: var(--background-color);
color: var(--text-color);
line-height: 1.5;
}
.navbar {
background-color: var(--surface-color);
box-shadow: var(--shadow-sm);
}
.navbar-brand {
font-weight: 700;
color: var(--primary-color) !important;
}
.container {
max-width: 1140px;
}
.card {
border: none;
border-radius: var(--border-radius);
box-shadow: var(--shadow-md);
background-color: var(--surface-color);
}
.card-header {
background-color: transparent;
border-bottom: 1px solid var(--border-color);
font-weight: 600;
padding: 1rem 1.5rem;
}
.card-body {
padding: 1.5rem;
}
.form-control, .form-select {
border-radius: var(--border-radius);
border: 1px solid var(--border-color);
padding: 0.75rem 1rem;
}
.form-control:focus {
border-color: var(--primary-color);
box-shadow: 0 0 0 0.25rem rgba(74, 144, 226, 0.25);
}
.btn {
border-radius: var(--border-radius);
padding: 0.75rem 1.5rem;
font-weight: 600;
transition: all 0.2s ease-in-out;
}
.btn-primary {
background-color: var(--primary-color);
border-color: var(--primary-color);
}
.btn-primary:hover {
opacity: 0.9;
}
.table {
border-color: var(--border-color);
}
.table th {
font-weight: 600;
background-color: #F9FAFB;
}
.table td, .table th {
vertical-align: middle;
}
.toast-container {
position: fixed;
top: 1rem;
right: 1rem;
z-index: 1090;
}
.toast {
border-radius: var(--border-radius);
border: none;
box-shadow: var(--shadow-md);
}
.toast-header {
border-bottom: 1px solid rgba(0,0,0,0.05);
}
.table .btn-sm {
padding: 0.25rem 0.6rem;
}
.feather-sm {
width: 16px;
height: 16px;
vertical-align: text-bottom;
}

90
assets/js/main.js Normal file
View File

@ -0,0 +1,90 @@
document.addEventListener('DOMContentLoaded', function () {
// --- Client-side validation for the add contact form ---
const addContactForm = document.getElementById('addContactForm');
if (addContactForm) {
addContactForm.addEventListener('submit', function (event) {
if (!validateForm(this)) {
event.preventDefault();
event.stopPropagation();
}
});
}
// --- Client-side validation for the edit contact form ---
const editContactForm = document.getElementById('editContactForm');
if (editContactForm) {
editContactForm.addEventListener('submit', function(event) {
if (!validateForm(this)) {
event.preventDefault();
event.stopPropagation();
}
});
}
// --- Edit Modal Handler ---
const editContactModal = document.getElementById('editContactModal');
if (editContactModal) {
editContactModal.addEventListener('show.bs.modal', function (event) {
const button = event.relatedTarget;
// Extract info from data-bs-* attributes
const id = button.getAttribute('data-id');
const name = button.getAttribute('data-name');
const email = button.getAttribute('data-email');
const phone = button.getAttribute('data-phone');
// Update the modal's content.
const modalTitle = editContactModal.querySelector('.modal-title');
const contactIdInput = editContactModal.querySelector('#edit_contact_id');
const nameInput = editContactModal.querySelector('#edit_name');
const emailInput = editContactModal.querySelector('#edit_email');
const phoneInput = editContactModal.querySelector('#edit_phone');
modalTitle.textContent = 'Edit Contact: ' + name;
contactIdInput.value = id;
nameInput.value = name;
emailInput.value = email;
phoneInput.value = phone;
});
}
// --- Toast notification handling ---
const toastEl = document.getElementById('notificationToast');
if (toastEl) {
const toast = new bootstrap.Toast(toastEl, { delay: 5000 });
toast.show();
}
// --- Feather Icons ---
feather.replace();
});
/**
* Validates a contact form (add or edit).
* @param {HTMLFormElement} form The form element to validate.
* @returns {boolean} True if valid, false otherwise.
*/
function validateForm(form) {
const name = form.querySelector('input[name="name"]');
const email = form.querySelector('input[name="email"]');
let isValid = true;
// Reset invalid states
name.classList.remove('is-invalid');
email.classList.remove('is-invalid');
// Validate Name
if (name.value.trim() === '') {
name.classList.add('is-invalid');
isValid = false;
}
// Validate Email
const emailPattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (email.value.trim() === '' || !emailPattern.test(email.value)) {
email.classList.add('is-invalid');
isValid = false;
}
return isValid;
}

373
index.php
View File

@ -1,131 +1,260 @@
<?php <?php
declare(strict_types=1); session_start();
@ini_set('display_errors', '1'); require_once 'db/config.php';
@error_reporting(E_ALL);
@date_default_timezone_set('UTC');
$phpVersion = PHP_VERSION; $notification = null;
$now = date('Y-m-d H:i:s');
try {
$pdo = db();
// Idempotent table creation
$pdo->exec("CREATE TABLE IF NOT EXISTS contacts (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(255) NOT NULL,
email VARCHAR(255) NOT NULL,
phone VARCHAR(50),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)");
// Handle POST requests
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
// Add a new contact
if (isset($_POST['add_contact'])) {
$name = trim($_POST['name']);
$email = trim($_POST['email']);
$phone = trim($_POST['phone']);
if (!empty($name) && !empty($email) && filter_var($email, FILTER_VALIDATE_EMAIL)) {
$stmt = $pdo->prepare("INSERT INTO contacts (name, email, phone) VALUES (?, ?, ?)");
$stmt->execute([$name, $email, $phone]);
$_SESSION['notification'] = ['type' => 'success', 'message' => 'Contact added successfully!'];
} else {
$_SESSION['notification'] = ['type' => 'danger', 'message' => 'Invalid data. Please check your input.'];
}
}
// Update an existing contact
elseif (isset($_POST['update_contact'])) {
$id = $_POST['contact_id'];
$name = trim($_POST['name']);
$email = trim($_POST['email']);
$phone = trim($_POST['phone']);
if (!empty($id) && !empty($name) && !empty($email) && filter_var($email, FILTER_VALIDATE_EMAIL)) {
$stmt = $pdo->prepare("UPDATE contacts SET name = ?, email = ?, phone = ? WHERE id = ?");
$stmt->execute([$name, $email, $phone, $id]);
$_SESSION['notification'] = ['type' => 'success', 'message' => 'Contact updated successfully!'];
} else {
$_SESSION['notification'] = ['type' => 'danger', 'message' => 'Invalid data. Please check your input.'];
}
}
// Delete a contact
elseif (isset($_POST['delete_contact'])) {
$id = $_POST['contact_id'];
if (!empty($id)) {
$stmt = $pdo->prepare("DELETE FROM contacts WHERE id = ?");
$stmt->execute([$id]);
$_SESSION['notification'] = ['type' => 'info', 'message' => 'Contact deleted.'];
}
}
header("Location: index.php");
exit;
}
// Check for notification from session
if (isset($_SESSION['notification'])) {
$notification = $_SESSION['notification'];
unset($_SESSION['notification']);
}
// Fetch all contacts
$stmt = $pdo->query("SELECT id, name, email, phone, created_at FROM contacts ORDER BY created_at DESC");
$contacts = $stmt->fetchAll();
} catch (PDOException $e) {
// For a real app, you'd log this error and show a user-friendly message.
$notification = ['type' => 'danger', 'message' => 'Database error: ' . $e->getMessage()];
$contacts = [];
}
?> ?>
<!doctype html> <!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" /> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>New Style</title> <title>CRM - Contact Management</title>
<link rel="preconnect" href="https://fonts.googleapis.com"> <meta name="description" content="A simple and effective CRM for managing your contacts.">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;700&display=swap" rel="stylesheet"> <!-- Open Graph -->
<style> <meta property="og:title" content="CRM - Contact Management">
:root { <meta property="og:description" content="A simple and effective CRM for managing your contacts.">
--bg-color-start: #6a11cb; <meta property="og:type" content="website">
--bg-color-end: #2575fc; <meta property="og:url" content="https://your-app-url.com/">
--text-color: #ffffff;
--card-bg-color: rgba(255, 255, 255, 0.01); <!-- Stylesheets -->
--card-border-color: rgba(255, 255, 255, 0.1); <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
} <link href="assets/css/custom.css?v=<?php echo time(); ?>" rel="stylesheet">
body {
margin: 0; <!-- Icons -->
font-family: 'Inter', sans-serif; <script src="https://cdn.jsdelivr.net/npm/feather-icons/dist/feather.min.js"></script>
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>
<main>
<div class="card"> <!-- Toast Notification -->
<h1>Analyzing your requirements and generating your website…</h1> <?php if ($notification): ?>
<div class="loader" role="status" aria-live="polite" aria-label="Applying initial changes"> <div class="toast-container position-fixed top-0 end-0 p-3">
<span class="sr-only">Loading…</span> <div id="notificationToast" class="toast align-items-center text-white bg-<?php echo htmlspecialchars($notification['type']); ?> border-0" role="alert" aria-live="assertive" aria-atomic="true">
</div> <div class="d-flex">
<p class="hint"><?= ($_SERVER['HTTP_HOST'] ?? '') === 'appwizzy.com' ? 'AppWiZZy' : 'Flatlogic' ?> AI is collecting your requirements and applying the first changes.</p> <div class="toast-body">
<p class="hint">This page will update automatically as the plan is implemented.</p> <?php echo htmlspecialchars($notification['message']); ?>
<p>Runtime: PHP <code><?= htmlspecialchars($phpVersion) ?></code> — UTC <code><?= htmlspecialchars($now) ?></code></p> </div>
<button type="button" class="btn-close btn-close-white me-2 m-auto" data-bs-dismiss="toast" aria-label="Close"></button>
</div>
</div>
</div> </div>
</main> <?php endif; ?>
<footer>
Page updated: <?= htmlspecialchars($now) ?> (UTC) <nav class="navbar navbar-expand-lg navbar-light">
</footer> <div class="container">
<a class="navbar-brand" href="#">
<i data-feather="box" class="me-2"></i>CRM
</a>
</div>
</nav>
<main class="container my-5">
<div class="row g-5">
<!-- Add Contact Form -->
<div class="col-lg-4">
<div class="card">
<div class="card-header">
<h4 class="mb-0">Add New Contact</h4>
</div>
<div class="card-body">
<form id="addContactForm" method="POST" action="index.php" novalidate>
<input type="hidden" name="add_contact" value="1">
<div class="mb-3">
<label for="name" class="form-label">Name</label>
<input type="text" class="form-control" id="name" name="name" required>
<div class="invalid-feedback">Please enter a name.</div>
</div>
<div class="mb-3">
<label for="email" class="form-label">Email</label>
<input type="email" class="form-control" id="email" name="email" required>
<div class="invalid-feedback">Please enter a valid email address.</div>
</div>
<div class="mb-3">
<label for="phone" class="form-label">Phone (Optional)</label>
<input type="text" class="form-control" id="phone" name="phone">
</div>
<div class="d-grid">
<button type="submit" class="btn btn-primary">
<i data-feather="plus-circle" class="me-2"></i>Add Contact
</button>
</div>
</form>
</div>
</div>
</div>
<!-- Contact List -->
<div class="col-lg-8">
<div class="card">
<div class="card-header">
<h4 class="mb-0">Contact List</h4>
</div>
<div class="card-body">
<div class="table-responsive">
<table class="table table-hover">
<thead>
<tr>
<th>Name</th>
<th>Email</th>
<th>Phone</th>
<th>Date Added</th>
<th class="text-end">Actions</th>
</tr>
</thead>
<tbody>
<?php if (empty($contacts)): ?>
<tr>
<td colspan="5" class="text-center text-muted">No contacts yet. Add one to get started!</td>
</tr>
<?php else: ?>
<?php foreach ($contacts as $contact): ?>
<tr>
<td><?php echo htmlspecialchars($contact['name']); ?></td>
<td><?php echo htmlspecialchars($contact['email']); ?></td>
<td><?php echo htmlspecialchars($contact['phone'] ?? ''); ?></td>
<td><?php echo date("M d, Y", strtotime($contact['created_at'])); ?></td>
<td class="text-end">
<button type="button" class="btn btn-sm btn-outline-primary me-2 edit-btn"
data-id="<?php echo $contact['id']; ?>"
data-name="<?php echo htmlspecialchars($contact['name']); ?>"
data-email="<?php echo htmlspecialchars($contact['email']); ?>"
data-phone="<?php echo htmlspecialchars($contact['phone'] ?? ''); ?>"
data-bs-toggle="modal" data-bs-target="#editContactModal">
<i data-feather="edit-2" class="feather-sm"></i> Edit
</button>
<form method="POST" action="index.php" class="d-inline" onsubmit="return confirm('Are you sure you want to delete this contact?');">
<input type="hidden" name="delete_contact" value="1">
<input type="hidden" name="contact_id" value="<?php echo $contact['id']; ?>">
<button type="submit" class="btn btn-sm btn-outline-danger">
<i data-feather="trash-2" class="feather-sm"></i> Delete
</button>
</form>
</td>
</tr>
<?php endforeach; ?>
<?php endif; ?>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
<!-- Edit Contact Modal -->
<div class="modal fade" id="editContactModal" tabindex="-1" aria-labelledby="editContactModalLabel" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<form id="editContactForm" method="POST" action="index.php">
<div class="modal-header">
<h5 class="modal-title" id="editContactModalLabel">Edit Contact</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<input type="hidden" name="update_contact" value="1">
<input type="hidden" id="edit_contact_id" name="contact_id">
<div class="mb-3">
<label for="edit_name" class="form-label">Name</label>
<input type="text" class="form-control" id="edit_name" name="name" required>
</div>
<div class="mb-3">
<label for="edit_email" class="form-label">Email</label>
<input type="email" class="form-control" id="edit_email" name="email" required>
</div>
<div class="mb-3">
<label for="edit_phone" class="form-label">Phone (Optional)</label>
<input type="text" class="form-control" id="edit_phone" name="phone">
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
<button type="submit" class="btn btn-primary">Save Changes</button>
</div>
</form>
</div>
</div>
</div>
</main>
<footer class="text-center text-muted py-4">
<small>Powered by Flatlogic</small>
</footer>
<!-- Scripts -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
<script src="assets/js/main.js?v=<?php echo time(); ?>"></script>
</body> </body>
</html> </html>