contacts.php
9.92 KB
<?php
require_once __DIR__ . '/../bootstrap.php';
require_once __DIR__ . '/../lib/feature_modules.php';
wp_feature_require_management(['contacts.view','contacts.manage']);
if (!hasPermission('contacts.view') && !isAdminRole()) {
http_response_code(403);
die('Access denied.');
}
$pageTitle = 'Contacts';
$canManageContacts = hasPermission('contacts.manage') || isAdminRole();
if (isset($_GET['export']) && $_GET['export'] === 'csv') {
$stmt = $pdo->query('SELECT contact_code, name, position, company, phone, email, type, notes, added_at FROM contacts ORDER BY name ASC');
$rows = [];
foreach ($stmt->fetchAll() as $r) $rows[] = [$r['contact_code'], $r['name'], $r['position'], $r['company'], $r['phone'], $r['email'], $r['type'], $r['notes'], $r['added_at']];
wp_feature_csv_download('contacts_' . date('Ymd_His') . '.csv', ['Code','Name','Position','Company','Phone','Email','Type','Notes','Added At'], $rows);
}
if ($_SERVER['REQUEST_METHOD'] === 'POST' && $canManageContacts) {
$action = (string)($_POST['action'] ?? '');
if ($action === 'save_contact') {
$id = (int)($_POST['id'] ?? 0);
$name = trim((string)($_POST['name'] ?? ''));
$position = trim((string)($_POST['position'] ?? ''));
$company = trim((string)($_POST['company'] ?? ''));
$phone = trim((string)($_POST['phone'] ?? ''));
$email = trim((string)($_POST['email'] ?? ''));
$type = trim((string)($_POST['type'] ?? 'Internal')) ?: 'Internal';
$notes = trim((string)($_POST['notes'] ?? ''));
if ($name === '') { wp_feature_flash_set('error', 'Name is required.'); app_redirect('management/contacts'); }
if ($id > 0) {
$stmt = $pdo->prepare('UPDATE contacts SET name=?, position=?, company=?, phone=?, email=?, type=?, notes=?, updated_at=NOW() WHERE id=?');
$stmt->execute([$name, $position, $company, $phone, $email, $type, $notes, $id]);
logActivity('contact_updated', 'contact', $id, 'Updated contact ' . $name);
wp_feature_flash_set('success', 'Contact updated.');
} else {
$code = wp_feature_generate_code('CON', 'contacts', 'contact_code');
$stmt = $pdo->prepare('INSERT INTO contacts (contact_code, name, position, company, phone, email, type, notes, added_by, added_by_name, added_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, NOW())');
$stmt->execute([$code, $name, $position, $company, $phone, $email, $type, $notes, wp_feature_current_user_id(), wp_feature_current_user_name()]);
logActivity('contact_created', 'contact', (int)$pdo->lastInsertId(), 'Created contact ' . $name);
wp_feature_flash_set('success', 'Contact created.');
}
app_redirect('management/contacts');
}
if ($action === 'delete_contact') {
$id = (int)($_POST['id'] ?? 0);
if ($id > 0) {
$stmt = $pdo->prepare('DELETE FROM contacts WHERE id = ?');
$stmt->execute([$id]);
logActivity('contact_deleted', 'contact', $id, 'Deleted contact #' . $id);
wp_feature_flash_set('success', 'Contact deleted.');
}
app_redirect('management/contacts');
}
}
$stmt = $pdo->query('SELECT * FROM contacts ORDER BY name ASC LIMIT 300');
$contacts = $stmt->fetchAll() ?: [];
$flash = wp_feature_flash_get();
$extraHead = '<style>
.contact-toolbar{display:flex;gap:10px;flex-wrap:wrap;justify-content:flex-end;align-items:center;}
.contact-search{max-width:360px;}
.contact-modal-grid{display:grid;grid-template-columns:1fr 1fr;gap:12px;}
@media(max-width:720px){.contact-modal-grid{grid-template-columns:1fr;}.contact-toolbar{justify-content:flex-start;}.contact-search{max-width:100%;}}
</style>';
include __DIR__ . '/../partials/header.php';
?>
<div class="content-header">
<div>
<h1 class="content-title">📇 Contacts</h1>
<p class="content-subtitle">Contact book for management. Dashboard only displays contacts; edits happen here.</p>
</div>
<div class="contact-toolbar">
<input class="input contact-search" type="search" id="contactFilter" placeholder="Search contacts...">
<a class="btn btn-secondary" href="<?= e(app_url('management/contacts?export=csv')) ?>">⬇️ Export CSV</a>
<?php if ($canManageContacts): ?><button class="btn btn-primary" type="button" data-open-contact-modal>➕ Add Contact</button><?php endif; ?>
</div>
</div>
<?php if ($flash): ?><div class="alert alert-<?= e($flash['type'] === 'error' ? 'error' : 'success') ?>" style="margin-bottom:16px;"><?= e($flash['message']) ?></div><?php endif; ?>
<div class="card">
<div class="card-header"><h3 class="card-title">Contact List (<?= count($contacts) ?>)</h3></div>
<div class="table-wrap">
<table class="table" style="min-width:1000px;" id="contactsTable">
<thead><tr><th>Name</th><th>Position</th><th>Company</th><th>Phone</th><th>Email</th><th>Type</th><th>Notes</th><th>Actions</th></tr></thead>
<tbody>
<?php if (!$contacts): ?><tr><td colspan="8" class="text-muted">No contacts found.</td></tr>
<?php else: foreach ($contacts as $row): ?>
<tr data-search="<?= e(strtolower(trim(($row['name'] ?? '') . ' ' . ($row['position'] ?? '') . ' ' . ($row['company'] ?? '') . ' ' . ($row['phone'] ?? '') . ' ' . ($row['email'] ?? '') . ' ' . ($row['type'] ?? '')))) ?>">
<td><strong><?= e($row['name']) ?></strong></td>
<td><?= e($row['position']) ?></td>
<td><?= e($row['company']) ?></td>
<td><?= e($row['phone']) ?></td>
<td><?= e($row['email']) ?></td>
<td><?= e($row['type']) ?></td>
<td><?= e(mb_strimwidth((string)$row['notes'], 0, 80, '…')) ?></td>
<td>
<?php if ($canManageContacts): ?>
<div class="d-flex gap-sm" style="flex-wrap:wrap;">
<button class="btn btn-secondary btn-xs" type="button" data-edit-contact='<?= e(json_encode($row, JSON_HEX_TAG|JSON_HEX_APOS|JSON_HEX_QUOT|JSON_HEX_AMP)) ?>'>Edit</button>
<form method="post" onsubmit="return confirm('Delete this contact?');" style="display:inline;"><input type="hidden" name="action" value="delete_contact"><input type="hidden" name="id" value="<?= (int)$row['id'] ?>"><button class="btn btn-danger btn-xs" type="submit">Delete</button></form>
</div>
<?php else: ?><span class="text-muted small">View only</span><?php endif; ?>
</td>
</tr>
<?php endforeach; endif; ?>
</tbody>
</table>
</div>
</div>
<?php if ($canManageContacts): ?>
<div class="modal-overlay" id="contactModal" style="display:none;">
<div class="modal modal-medium">
<div class="modal-header"><h3 class="modal-title" id="contactModalTitle">Add Contact</h3><button class="modal-close" type="button" data-close-contact-modal>×</button></div>
<form method="post" id="contactForm">
<input type="hidden" name="action" value="save_contact">
<input type="hidden" name="id" id="contact_id" value="">
<div class="modal-body contact-modal-grid">
<div><label class="field-label">Name</label><input class="input" type="text" name="name" id="contact_name" required></div>
<div><label class="field-label">Position</label><input class="input" type="text" name="position" id="contact_position"></div>
<div><label class="field-label">Company</label><input class="input" type="text" name="company" id="contact_company"></div>
<div><label class="field-label">Phone</label><input class="input" type="text" name="phone" id="contact_phone"></div>
<div><label class="field-label">Email</label><input class="input" type="email" name="email" id="contact_email"></div>
<div><label class="field-label">Type</label><select class="input" name="type" id="contact_type"><?php foreach (['Internal','Supplier','Customer','Emergency','Other'] as $opt): ?><option value="<?= e($opt) ?>"><?= e($opt) ?></option><?php endforeach; ?></select></div>
<div style="grid-column:1 / -1;"><label class="field-label">Notes</label><textarea class="input" name="notes" id="contact_notes" rows="3"></textarea></div>
</div>
<div class="modal-footer"><button class="btn btn-secondary" type="button" data-close-contact-modal>Cancel</button><button class="btn btn-primary" type="submit">Save Contact</button></div>
</form>
</div>
</div>
<script>
(function(){
const modal=document.getElementById('contactModal');
const form=document.getElementById('contactForm');
function setVal(id,val){ const el=document.getElementById(id); if(el) el.value=val??''; }
function openContact(data){
if(!modal||!form) return; form.reset();
document.getElementById('contactModalTitle').textContent=data?'Edit Contact':'Add Contact';
setVal('contact_id',data?.id||''); setVal('contact_name',data?.name||''); setVal('contact_position',data?.position||''); setVal('contact_company',data?.company||''); setVal('contact_phone',data?.phone||''); setVal('contact_email',data?.email||''); setVal('contact_type',data?.type||'Internal'); setVal('contact_notes',data?.notes||'');
modal.style.display='flex';
}
function closeContact(){ if(modal) modal.style.display='none'; }
document.addEventListener('click',function(ev){
if(ev.target.closest('[data-open-contact-modal]')){ ev.preventDefault(); openContact(null); }
const edit=ev.target.closest('[data-edit-contact]'); if(edit){ ev.preventDefault(); try{ openContact(JSON.parse(edit.getAttribute('data-edit-contact')||'{}')); }catch(e){} }
if(ev.target.closest('[data-close-contact-modal]')){ ev.preventDefault(); closeContact(); }
if(ev.target===modal) closeContact();
});
})();
</script>
<?php endif; ?>
<script>
(function(){
const input=document.getElementById('contactFilter');
const rows=document.querySelectorAll('#contactsTable tbody tr[data-search]');
input?.addEventListener('input',function(){ const q=this.value.trim().toLowerCase(); rows.forEach(r=>{ r.style.display=!q||(r.dataset.search||'').includes(q)?'':'none'; }); });
})();
</script>
<?php include __DIR__ . '/../partials/footer.php'; ?>